GLRootView.java revision f9a0a4306d589b4a4e20554fed512a603426bfa1
1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/*
2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project
3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License");
5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License.
6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at
7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *      http://www.apache.org/licenses/LICENSE-2.0
9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin *
10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software
11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS,
12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and
14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License.
15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */
16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.anim.CanvasAnimation;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.GalleryUtils;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.app.Activity;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.content.Context;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.PixelFormat;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.graphics.Rect;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.opengl.GLSurfaceView;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.Process;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.os.SystemClock;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.util.AttributeSet;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.util.DisplayMetrics;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.view.MotionEvent;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.LinkedList;
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.locks.ReentrantLock;
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.egl.EGLConfig;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.opengles.GL10;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.opengles.GL11;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The root component of all <code>GLView</code>s. The rendering is done in GL
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// thread while the event handling is done in the main thread.  To synchronize
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// the two threads, the entry points of this package need to synchronize on the
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// <code>GLRootView</code> instance unless it can be proved that the rendering
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// thread won't access the same thing as the method. The entry points include:
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// (1) The public methods of HeadUpDisplay
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// (2) The public methods of CameraHeadUpDisplay
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// (3) The overridden methods in GLRootView.
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class GLRootView extends GLSurfaceView
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        implements GLSurfaceView.Renderer, GLRoot {
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "GLRootView";
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final boolean DEBUG_FPS = false;
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mFrameCount = 0;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mFrameCountingStart = 0;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final boolean DEBUG_INVALIDATE = false;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mInvalidateColor = 0;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final boolean DEBUG_DRAWING_STAT = false;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int FLAG_INITIALIZED = 1;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int FLAG_NEED_LAYOUT = 2;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GL11 mGL;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GLCanvasImpl mCanvas;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GLView mContentView;
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private DisplayMetrics mDisplayMetrics;
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mFlags = FLAG_NEED_LAYOUT;
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private volatile boolean mRenderRequested = false;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Rect mClipRect = new Rect();
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mClipRetryCount = 0;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final GalleryEGLConfigChooser mEglConfigChooser =
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new GalleryEGLConfigChooser();
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ArrayList<CanvasAnimation> mAnimations =
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new ArrayList<CanvasAnimation>();
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final LinkedList<OnGLIdleListener> mIdleListeners =
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new LinkedList<OnGLIdleListener>();
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final IdleRunner mIdleRunner = new IdleRunner();
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ReentrantLock mRenderLock = new ReentrantLock();
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int TARGET_FRAME_TIME = 33;
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mLastDrawFinishTime;
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mInDownState = false;
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public GLRootView(Context context) {
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        this(context, null);
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public GLRootView(Context context, AttributeSet attrs) {
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(context, attrs);
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mFlags |= FLAG_INITIALIZED;
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setBackgroundDrawable(null);
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setEGLConfigChooser(mEglConfigChooser);
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setRenderer(this);
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        getHolder().setFormat(PixelFormat.RGB_565);
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Uncomment this to enable gl error check.
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //setDebugFlags(DEBUG_CHECK_GL_ERROR);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public GalleryEGLConfigChooser getEGLConfigChooser() {
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mEglConfigChooser;
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean hasStencil() {
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getEGLConfigChooser().getStencilBits() > 0;
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void registerLaunchedAnimation(CanvasAnimation animation) {
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Register the newly launched animation so that we can set the start
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // time more precisely. (Usually, it takes much longer for first
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // rendering, so we set the animation start time as the time we
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // complete rendering)
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mAnimations.add(animation);
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void addOnGLIdleListener(OnGLIdleListener listener) {
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mIdleListeners) {
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIdleListeners.addLast(listener);
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIdleRunner.enable();
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setContentPane(GLView content) {
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentView == content) return;
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentView != null) {
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mInDownState) {
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                long now = SystemClock.uptimeMillis();
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MotionEvent cancelEvent = MotionEvent.obtain(
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        now, now, MotionEvent.ACTION_CANCEL, 0, 0, 0);
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mContentView.dispatchTouchEvent(cancelEvent);
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                cancelEvent.recycle();
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mInDownState = false;
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentView.detachFromRoot();
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            BasicTexture.yieldAllTextures();
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContentView = content;
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (content != null) {
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            content.attachToRoot(this);
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestLayoutContentPane();
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public GLView getContentPane() {
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mContentView;
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void requestRender() {
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (DEBUG_INVALIDATE) {
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            StackTraceElement e = Thread.currentThread().getStackTrace()[4];
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            String caller = e.getFileName() + ":" + e.getLineNumber() + " ";
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Log.d(TAG, "invalidate: " + caller);
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mRenderRequested) return;
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderRequested = true;
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super.requestRender();
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void requestLayoutContentPane() {
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderLock.lock();
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mContentView == null || (mFlags & FLAG_NEED_LAYOUT) != 0) return;
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // "View" system will invoke onLayout() for initialization(bug ?), we
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // have to ignore it since the GLThread is not ready yet.
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if ((mFlags & FLAG_INITIALIZED) == 0) return;
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFlags |= FLAG_NEED_LAYOUT;
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestRender();
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } finally {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRenderLock.unlock();
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void layoutContentPane() {
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mFlags &= ~FLAG_NEED_LAYOUT;
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int width = getWidth();
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int height = getHeight();
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Log.i(TAG, "layout content pane " + width + "x" + height);
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentView != null && width != 0 && height != 0) {
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentView.layout(0, 0, width, height);
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Uncomment this to dump the view hierarchy.
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        //mContentView.dumpTree("");
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected void onLayout(
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean changed, int left, int top, int right, int bottom) {
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (changed) requestLayoutContentPane();
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Called when the context is created, possibly after automatic destruction.
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // This is a GLSurfaceView.Renderer callback
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onSurfaceCreated(GL10 gl1, EGLConfig config) {
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GL11 gl = (GL11) gl1;
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mGL != null) {
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // The GL Object has changed
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Log.i(TAG, "GLObject has changed from " + mGL + " to " + gl);
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mGL = gl;
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCanvas = new GLCanvasImpl(gl);
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!DEBUG_FPS) {
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /**
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * Called when the OpenGL surface is recreated without destroying the
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * context.
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // This is a GLSurfaceView.Renderer callback
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onSurfaceChanged(GL10 gl1, int width, int height) {
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Log.i(TAG, "onSurfaceChanged: " + width + "x" + height
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                + ", gl10: " + gl1.toString());
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY);
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GalleryUtils.setRenderThread();
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        GL11 gl = (GL11) gl1;
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Utils.assertTrue(mGL == gl);
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCanvas.setSize(width, height);
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mClipRect.set(0, 0, width, height);
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mClipRetryCount = 2;
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void outputFps() {
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long now = System.nanoTime();
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mFrameCountingStart == 0) {
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFrameCountingStart = now;
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if ((now - mFrameCountingStart) > 1000000000) {
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Log.d(TAG, "fps: " + (double) mFrameCount
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    * 1000000000 / (now - mFrameCountingStart));
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFrameCountingStart = now;
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFrameCount = 0;
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ++mFrameCount;
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onDrawFrame(GL10 gl) {
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderLock.lock();
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            onDrawFrameLocked(gl);
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } finally {
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRenderLock.unlock();
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long end = SystemClock.uptimeMillis();
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mLastDrawFinishTime != 0) {
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long wait = mLastDrawFinishTime + TARGET_FRAME_TIME - end;
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (wait > 0) {
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                SystemClock.sleep(wait);
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLastDrawFinishTime = SystemClock.uptimeMillis();
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void onDrawFrameLocked(GL10 gl) {
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (DEBUG_FPS) outputFps();
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // release the unbound textures and deleted buffers.
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCanvas.deleteRecycledResources();
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // reset texture upload limit
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        UploadedTexture.resetUploadLimit();
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderRequested = false;
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if ((mFlags & FLAG_NEED_LAYOUT) != 0) layoutContentPane();
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // OpenGL seems having a bug causing us not being able to reset the
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // scissor box in "onSurfaceChanged()". We have to do it in the second
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // onDrawFrame().
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mClipRetryCount > 0) {
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            --mClipRetryCount;
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Rect clip = mClipRect;
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            gl.glScissor(clip.left, clip.top, clip.width(), clip.height());
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCanvas.setCurrentAnimationTimeMillis(SystemClock.uptimeMillis());
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentView != null) {
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin           mContentView.render(mCanvas);
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mAnimations.isEmpty()) {
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long now = SystemClock.uptimeMillis();
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = 0, n = mAnimations.size(); i < n; i++) {
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mAnimations.get(i).setStartTime(now);
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mAnimations.clear();
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (UploadedTexture.uploadLimitReached()) {
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestRender();
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mIdleListeners) {
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (!mRenderRequested && !mIdleListeners.isEmpty()) {
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIdleRunner.enable();
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (DEBUG_INVALIDATE) {
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mCanvas.fillRect(10, 10, 5, 5, mInvalidateColor);
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mInvalidateColor = ~mInvalidateColor;
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (DEBUG_DRAWING_STAT) {
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mCanvas.dumpStatisticsAndClear();
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean dispatchTouchEvent(MotionEvent event) {
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int action = event.getAction();
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (action == MotionEvent.ACTION_CANCEL
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || action == MotionEvent.ACTION_UP) {
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mInDownState = false;
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else if (!mInDownState && action != MotionEvent.ACTION_DOWN) {
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderLock.lock();
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // If this has been detached from root, we don't need to handle event
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean handled = mContentView != null
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    && mContentView.dispatchTouchEvent(event);
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (action == MotionEvent.ACTION_DOWN && handled) {
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mInDownState = true;
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return handled;
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } finally {
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRenderLock.unlock();
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public DisplayMetrics getDisplayMetrics() {
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mDisplayMetrics == null) {
361f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDisplayMetrics = new DisplayMetrics();
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ((Activity) getContext()).getWindowManager()
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    .getDefaultDisplay().getMetrics(mDisplayMetrics);
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mDisplayMetrics;
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public GLCanvas getCanvas() {
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mCanvas;
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class IdleRunner implements Runnable {
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // true if the idle runner is in the queue
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mActive = false;
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            OnGLIdleListener listener;
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            synchronized (mIdleListeners) {
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mActive = false;
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mRenderRequested) return;
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mIdleListeners.isEmpty()) return;
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                listener = mIdleListeners.removeFirst();
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mRenderLock.lock();
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            try {
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (!listener.onGLIdle(GLRootView.this, mCanvas)) return;
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } finally {
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mRenderLock.unlock();
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            synchronized (mIdleListeners) {
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIdleListeners.addLast(listener);
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                enable();
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void enable() {
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Who gets the flag can add it to the queue
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mActive) return;
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mActive = true;
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            queueEvent(this);
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void lockRenderThread() {
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderLock.lock();
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void unlockRenderThread() {
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mRenderLock.unlock();
413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
415