OSDN Git Service

f78e6e6bfc5903cd9cb41fecb4780df1c768e1f8
[android-x86/packages-apps-Camera2.git] / src / com / android / gallery3d / ui / GLRootView.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package com.android.gallery3d.ui;
18
19 import android.content.Context;
20 import android.graphics.Matrix;
21 import android.graphics.PixelFormat;
22 import android.opengl.GLSurfaceView;
23 import android.os.Process;
24 import android.os.SystemClock;
25 import android.util.AttributeSet;
26 import android.view.MotionEvent;
27 import android.view.SurfaceHolder;
28
29 import com.android.gallery3d.anim.CanvasAnimation;
30 import com.android.gallery3d.common.Utils;
31 import com.android.gallery3d.util.GalleryUtils;
32 import com.android.gallery3d.util.Profile;
33
34 import java.util.ArrayDeque;
35 import java.util.ArrayList;
36 import java.util.concurrent.locks.Condition;
37 import java.util.concurrent.locks.ReentrantLock;
38
39 import javax.microedition.khronos.egl.EGLConfig;
40 import javax.microedition.khronos.opengles.GL10;
41 import javax.microedition.khronos.opengles.GL11;
42
43 // The root component of all <code>GLView</code>s. The rendering is done in GL
44 // thread while the event handling is done in the main thread.  To synchronize
45 // the two threads, the entry points of this package need to synchronize on the
46 // <code>GLRootView</code> instance unless it can be proved that the rendering
47 // thread won't access the same thing as the method. The entry points include:
48 // (1) The public methods of HeadUpDisplay
49 // (2) The public methods of CameraHeadUpDisplay
50 // (3) The overridden methods in GLRootView.
51 public class GLRootView extends GLSurfaceView
52         implements GLSurfaceView.Renderer, GLRoot {
53     private static final String TAG = "GLRootView";
54
55     private static final boolean DEBUG_FPS = false;
56     private int mFrameCount = 0;
57     private long mFrameCountingStart = 0;
58
59     private static final boolean DEBUG_INVALIDATE = false;
60     private int mInvalidateColor = 0;
61
62     private static final boolean DEBUG_DRAWING_STAT = false;
63
64     private static final boolean DEBUG_PROFILE = false;
65     private static final boolean DEBUG_PROFILE_SLOW_ONLY = false;
66
67     private static final int FLAG_INITIALIZED = 1;
68     private static final int FLAG_NEED_LAYOUT = 2;
69
70     private GL11 mGL;
71     private GLCanvas mCanvas;
72     private GLView mContentView;
73
74     private OrientationSource mOrientationSource;
75     // mCompensation is the difference between the UI orientation on GLCanvas
76     // and the framework orientation. See OrientationManager for details.
77     private int mCompensation;
78     // mCompensationMatrix maps the coordinates of touch events. It is kept sync
79     // with mCompensation.
80     private Matrix mCompensationMatrix = new Matrix();
81     private int mDisplayRotation;
82
83     // The value which will become mCompensation in next layout.
84     private int mPendingCompensation;
85
86     private int mFlags = FLAG_NEED_LAYOUT;
87     private volatile boolean mRenderRequested = false;
88
89     private final GalleryEGLConfigChooser mEglConfigChooser =
90             new GalleryEGLConfigChooser();
91
92     private final ArrayList<CanvasAnimation> mAnimations =
93             new ArrayList<CanvasAnimation>();
94
95     private final ArrayDeque<OnGLIdleListener> mIdleListeners =
96             new ArrayDeque<OnGLIdleListener>();
97
98     private final IdleRunner mIdleRunner = new IdleRunner();
99
100     private final ReentrantLock mRenderLock = new ReentrantLock();
101     private final Condition mFreezeCondition =
102             mRenderLock.newCondition();
103     private boolean mFreeze;
104
105     private long mLastDrawFinishTime;
106     private boolean mInDownState = false;
107
108     public GLRootView(Context context) {
109         this(context, null);
110     }
111
112     public GLRootView(Context context, AttributeSet attrs) {
113         super(context, attrs);
114         mFlags |= FLAG_INITIALIZED;
115         setBackgroundDrawable(null);
116         setEGLConfigChooser(mEglConfigChooser);
117         setRenderer(this);
118         getHolder().setFormat(PixelFormat.RGB_565);
119
120         // Uncomment this to enable gl error check.
121         // setDebugFlags(DEBUG_CHECK_GL_ERROR);
122     }
123
124     @Override
125     public void registerLaunchedAnimation(CanvasAnimation animation) {
126         // Register the newly launched animation so that we can set the start
127         // time more precisely. (Usually, it takes much longer for first
128         // rendering, so we set the animation start time as the time we
129         // complete rendering)
130         mAnimations.add(animation);
131     }
132
133     @Override
134     public void addOnGLIdleListener(OnGLIdleListener listener) {
135         synchronized (mIdleListeners) {
136             mIdleListeners.addLast(listener);
137             mIdleRunner.enable();
138         }
139     }
140
141     @Override
142     public void setContentPane(GLView content) {
143         if (mContentView == content) return;
144         if (mContentView != null) {
145             if (mInDownState) {
146                 long now = SystemClock.uptimeMillis();
147                 MotionEvent cancelEvent = MotionEvent.obtain(
148                         now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
149                 mContentView.dispatchTouchEvent(cancelEvent);
150                 cancelEvent.recycle();
151                 mInDownState = false;
152             }
153             mContentView.detachFromRoot();
154             BasicTexture.yieldAllTextures();
155         }
156         mContentView = content;
157         if (content != null) {
158             content.attachToRoot(this);
159             requestLayoutContentPane();
160         }
161     }
162
163     @Override
164     public void requestRender() {
165         if (DEBUG_INVALIDATE) {
166             StackTraceElement e = Thread.currentThread().getStackTrace()[4];
167             String caller = e.getFileName() + ":" + e.getLineNumber() + " ";
168             Log.d(TAG, "invalidate: " + caller);
169         }
170         if (mRenderRequested) return;
171         mRenderRequested = true;
172         super.requestRender();
173     }
174
175     @Override
176     public void requestLayoutContentPane() {
177         mRenderLock.lock();
178         try {
179             if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
180
181             // "View" system will invoke onLayout() for initialization(bug ?), we
182             // have to ignore it since the GLThread is not ready yet.
183             if ((mFlags & FLAG_INITIALIZED) == 0) return;
184
185             mFlags |= FLAG_NEED_LAYOUT;
186             requestRender();
187         } finally {
188             mRenderLock.unlock();
189         }
190     }
191
192     private void layoutContentPane() {
193         mFlags &= ~FLAG_NEED_LAYOUT;
194
195         int w = getWidth();
196         int h = getHeight();
197         int displayRotation = 0;
198         int compensation = 0;
199
200         // Get the new orientation values
201         if (mOrientationSource != null) {
202             displayRotation = mOrientationSource.getDisplayRotation();
203             compensation = mOrientationSource.getCompensation();
204         } else {
205             displayRotation = 0;
206             compensation = 0;
207         }
208
209         if (mCompensation != compensation) {
210             mCompensation = compensation;
211             if (mCompensation % 180 != 0) {
212                 mCompensationMatrix.setRotate(mCompensation);
213                 // move center to origin before rotation
214                 mCompensationMatrix.preTranslate(-w / 2, -h / 2);
215                 // align with the new origin after rotation
216                 mCompensationMatrix.postTranslate(h / 2, w / 2);
217             } else {
218                 mCompensationMatrix.setRotate(mCompensation, w / 2, h / 2);
219             }
220         }
221         mDisplayRotation = displayRotation;
222
223         // Do the actual layout.
224         if (mCompensation % 180 != 0) {
225             int tmp = w;
226             w = h;
227             h = tmp;
228         }
229         Log.i(TAG, "layout content pane " + w + "x" + h
230                 + " (compensation " + mCompensation + ")");
231         if (mContentView != null && w != 0 && h != 0) {
232             mContentView.layout(0, 0, w, h);
233         }
234         // Uncomment this to dump the view hierarchy.
235         //mContentView.dumpTree("");
236     }
237
238     @Override
239     protected void onLayout(
240             boolean changed, int left, int top, int right, int bottom) {
241         if (changed) requestLayoutContentPane();
242     }
243
244     /**
245      * Called when the context is created, possibly after automatic destruction.
246      */
247     // This is a GLSurfaceView.Renderer callback
248     @Override
249     public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
250         GL11 gl = (GL11) gl1;
251         if (mGL != null) {
252             // The GL Object has changed
253             Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
254         }
255         mRenderLock.lock();
256         try {
257             mGL = gl;
258             mCanvas = new GLCanvasImpl(gl);
259             BasicTexture.invalidateAllTextures();
260         } finally {
261             mRenderLock.unlock();
262         }
263
264         if (DEBUG_FPS || DEBUG_PROFILE) {
265             setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
266         } else {
267             setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
268         }
269     }
270
271     /**
272      * Called when the OpenGL surface is recreated without destroying the
273      * context.
274      */
275     // This is a GLSurfaceView.Renderer callback
276     @Override
277     public void onSurfaceChanged(GL10 gl1, int width, int height) {
278         Log.i(TAG, "onSurfaceChanged: " + width + "x" + height
279                 + ", gl10: " + gl1.toString());
280         Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
281         GalleryUtils.setRenderThread();
282         if (DEBUG_PROFILE) {
283             Log.d(TAG, "Start profiling");
284             Profile.enable(20);  // take a sample every 20ms
285         }
286         GL11 gl = (GL11) gl1;
287         Utils.assertTrue(mGL == gl);
288
289         mCanvas.setSize(width, height);
290     }
291
292     private void outputFps() {
293         long now = System.nanoTime();
294         if (mFrameCountingStart == 0) {
295             mFrameCountingStart = now;
296         } else if ((now - mFrameCountingStart) > 1000000000) {
297             Log.d(TAG, "fps: " + (double) mFrameCount
298                     * 1000000000 / (now - mFrameCountingStart));
299             mFrameCountingStart = now;
300             mFrameCount = 0;
301         }
302         ++mFrameCount;
303     }
304
305     @Override
306     public void onDrawFrame(GL10 gl) {
307         AnimationTime.update();
308         long t0;
309         if (DEBUG_PROFILE_SLOW_ONLY) {
310             Profile.hold();
311             t0 = System.nanoTime();
312         }
313         mRenderLock.lock();
314
315         while (mFreeze) {
316             mFreezeCondition.awaitUninterruptibly();
317         }
318
319         try {
320             onDrawFrameLocked(gl);
321         } finally {
322             mRenderLock.unlock();
323         }
324
325         if (DEBUG_PROFILE_SLOW_ONLY) {
326             long t = System.nanoTime();
327             long durationInMs = (t - mLastDrawFinishTime) / 1000000;
328             long durationDrawInMs = (t - t0) / 1000000;
329             mLastDrawFinishTime = t;
330
331             if (durationInMs > 34) {  // 34ms -> we skipped at least 2 frames
332                 Log.v(TAG, "----- SLOW (" + durationDrawInMs + "/" +
333                         durationInMs + ") -----");
334                 Profile.commit();
335             } else {
336                 Profile.drop();
337             }
338         }
339     }
340
341     private void onDrawFrameLocked(GL10 gl) {
342         if (DEBUG_FPS) outputFps();
343
344         // release the unbound textures and deleted buffers.
345         mCanvas.deleteRecycledResources();
346
347         // reset texture upload limit
348         UploadedTexture.resetUploadLimit();
349
350         mRenderRequested = false;
351
352         if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
353
354         mCanvas.save(GLCanvas.SAVE_FLAG_ALL);
355         rotateCanvas(-mCompensation);
356         if (mContentView != null) {
357            mContentView.render(mCanvas);
358         }
359         mCanvas.restore();
360
361         if (!mAnimations.isEmpty()) {
362             long now = AnimationTime.get();
363             for (int i = 0, n = mAnimations.size(); i < n; i++) {
364                 mAnimations.get(i).setStartTime(now);
365             }
366             mAnimations.clear();
367         }
368
369         if (UploadedTexture.uploadLimitReached()) {
370             requestRender();
371         }
372
373         synchronized (mIdleListeners) {
374             if (!mIdleListeners.isEmpty()) mIdleRunner.enable();
375         }
376
377         if (DEBUG_INVALIDATE) {
378             mCanvas.fillRect(10, 10, 5, 5, mInvalidateColor);
379             mInvalidateColor = ~mInvalidateColor;
380         }
381
382         if (DEBUG_DRAWING_STAT) {
383             mCanvas.dumpStatisticsAndClear();
384         }
385     }
386
387     private void rotateCanvas(int degrees) {
388         if (degrees == 0) return;
389         int w = getWidth();
390         int h = getHeight();
391         int cx = w / 2;
392         int cy = h / 2;
393         mCanvas.translate(cx, cy);
394         mCanvas.rotate(degrees, 0, 0, 1);
395         if (degrees % 180 != 0) {
396             mCanvas.translate(-cy, -cx);
397         } else {
398             mCanvas.translate(-cx, -cy);
399         }
400     }
401
402     @Override
403     public boolean dispatchTouchEvent(MotionEvent event) {
404         if (!isEnabled()) return false;
405
406         int action = event.getAction();
407         if (action == MotionEvent.ACTION_CANCEL
408                 || action == MotionEvent.ACTION_UP) {
409             mInDownState = false;
410         } else if (!mInDownState && action != MotionEvent.ACTION_DOWN) {
411             return false;
412         }
413
414         if (mCompensation != 0) {
415             event.transform(mCompensationMatrix);
416         }
417
418         mRenderLock.lock();
419         try {
420             // If this has been detached from root, we don't need to handle event
421             boolean handled = mContentView != null
422                     && mContentView.dispatchTouchEvent(event);
423             if (action == MotionEvent.ACTION_DOWN && handled) {
424                 mInDownState = true;
425             }
426             return handled;
427         } finally {
428             mRenderLock.unlock();
429         }
430     }
431
432     private class IdleRunner implements Runnable {
433         // true if the idle runner is in the queue
434         private boolean mActive = false;
435
436         @Override
437         public void run() {
438             OnGLIdleListener listener;
439             synchronized (mIdleListeners) {
440                 mActive = false;
441                 if (mIdleListeners.isEmpty()) return;
442                 listener = mIdleListeners.removeFirst();
443             }
444             mRenderLock.lock();
445             try {
446                 if (!listener.onGLIdle(mCanvas, mRenderRequested)) return;
447             } finally {
448                 mRenderLock.unlock();
449             }
450             synchronized (mIdleListeners) {
451                 mIdleListeners.addLast(listener);
452                 if (!mRenderRequested) enable();
453             }
454         }
455
456         public void enable() {
457             // Who gets the flag can add it to the queue
458             if (mActive) return;
459             mActive = true;
460             queueEvent(this);
461         }
462     }
463
464     @Override
465     public void lockRenderThread() {
466         mRenderLock.lock();
467     }
468
469     @Override
470     public void unlockRenderThread() {
471         mRenderLock.unlock();
472     }
473
474     @Override
475     public void onPause() {
476         unfreeze();
477         super.onPause();
478         if (DEBUG_PROFILE) {
479             Log.d(TAG, "Stop profiling");
480             Profile.disableAll();
481             Profile.dumpToFile("/sdcard/gallery.prof");
482             Profile.reset();
483         }
484     }
485
486     @Override
487     public void setOrientationSource(OrientationSource source) {
488         mOrientationSource = source;
489     }
490
491     @Override
492     public int getDisplayRotation() {
493         return mDisplayRotation;
494     }
495
496     @Override
497     public int getCompensation() {
498         return mCompensation;
499     }
500
501     @Override
502     public Matrix getCompensationMatrix() {
503         return mCompensationMatrix;
504     }
505
506     @Override
507     public void freeze() {
508         mRenderLock.lock();
509         mFreeze = true;
510         mRenderLock.unlock();
511     }
512
513     @Override
514     public void unfreeze() {
515         mRenderLock.lock();
516         mFreeze = false;
517         mFreezeCondition.signalAll();
518         mRenderLock.unlock();
519     }
520
521     @Override
522     public void setLightsOutMode(boolean enabled) {
523         int flags = enabled
524                 ? SYSTEM_UI_FLAG_LOW_PROFILE
525                 | SYSTEM_UI_FLAG_FULLSCREEN
526                 | SYSTEM_UI_FLAG_LAYOUT_STABLE
527                 : 0;
528         setSystemUiVisibility(flags);
529     }
530
531     // We need to unfreeze in the following methods and in onPause().
532     // These methods will wait on GLThread. If we have freezed the GLRootView,
533     // the GLThread will wait on main thread to call unfreeze and cause dead
534     // lock.
535     @Override
536     public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
537         unfreeze();
538         super.surfaceChanged(holder, format, w, h);
539     }
540
541     @Override
542     public void surfaceCreated(SurfaceHolder holder) {
543         unfreeze();
544         super.surfaceCreated(holder);
545     }
546
547     @Override
548     public void surfaceDestroyed(SurfaceHolder holder) {
549         unfreeze();
550         super.surfaceDestroyed(holder);
551     }
552
553     @Override
554     protected void onDetachedFromWindow() {
555         unfreeze();
556         super.onDetachedFromWindow();
557     }
558
559     @Override
560     protected void finalize() throws Throwable {
561         try {
562             unfreeze();
563         } finally {
564             super.finalize();
565         }
566     }
567 }