diff options
author | John Reck <jreck@google.com> | 2013-11-05 13:27:50 -0800 |
---|---|---|
committer | John Reck <jreck@google.com> | 2013-12-09 15:57:09 -0800 |
commit | cec24ae16e9a0a7c3075f1a8d9149bb7fb3813fc (patch) | |
tree | 9b2287a705b0634197262c13433f5c32aa848bdc | |
parent | 4598ea4e5e6b2accce5165a76f5e2d04ce46c74c (diff) | |
download | frameworks_base-cec24ae16e9a0a7c3075f1a8d9149bb7fb3813fc.zip frameworks_base-cec24ae16e9a0a7c3075f1a8d9149bb7fb3813fc.tar.gz frameworks_base-cec24ae16e9a0a7c3075f1a8d9149bb7fb3813fc.tar.bz2 |
RenderThread work
Hacky prototype needs a private API to enable
Change-Id: I21e0ddf3cdbd38a4036354b5d6012449e1a34849
26 files changed, 1187 insertions, 129 deletions
diff --git a/core/java/android/view/GLRenderer.java b/core/java/android/view/GLRenderer.java index a195231..70428bc 100644 --- a/core/java/android/view/GLRenderer.java +++ b/core/java/android/view/GLRenderer.java @@ -635,11 +635,11 @@ public class GLRenderer extends HardwareRenderer { GLRenderer(boolean translucent) { mTranslucent = translucent; - loadSystemProperties(null); + loadSystemProperties(); } @Override - boolean loadSystemProperties(Surface surface) { + boolean loadSystemProperties() { boolean value; boolean changed = false; @@ -1102,11 +1102,6 @@ public class GLRenderer extends HardwareRenderer { } @Override - HardwareCanvas getCanvas() { - return mCanvas; - } - - @Override void setName(String name) { mName = name; } @@ -1129,6 +1124,66 @@ public class GLRenderer extends HardwareRenderer { } @Override + void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, + HardwareDrawCallbacks callbacks, Rect dirty) { + if (canDraw()) { + if (!hasDirtyRegions()) { + dirty = null; + } + + // We are already on the correct thread + final int surfaceState = checkRenderContextUnsafe(); + if (surfaceState != SURFACE_STATE_ERROR) { + HardwareCanvas canvas = mCanvas; + + if (mProfileEnabled) { + mProfileLock.lock(); + } + + dirty = beginFrame(canvas, dirty, surfaceState); + + int saveCount = 0; + int status = DisplayList.STATUS_DONE; + + long start = getSystemTime(); + try { + status = prepareFrame(dirty); + + saveCount = canvas.save(); + callbacks.onHardwarePreDraw(canvas); + + status |= drawDisplayList(attachInfo, canvas, displayList, status); + } catch (Exception e) { + Log.e(LOG_TAG, "An error has occurred while drawing:", e); + } finally { + callbacks.onHardwarePostDraw(canvas); + canvas.restoreToCount(saveCount); + + mDrawDelta = getSystemTime() - start; + + if (mDrawDelta > 0) { + mFrameCount++; + + debugOverdraw(attachInfo, dirty, canvas, displayList); + debugDirtyRegions(dirty, canvas); + drawProfileData(attachInfo); + } + } + + onPostDraw(); + + swapBuffers(status); + + if (mProfileEnabled) { + mProfileLock.unlock(); + } + + attachInfo.mIgnoreDirtyState = false; + } + } + } + + @Override void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { if (canDraw()) { @@ -1144,7 +1199,6 @@ public class GLRenderer extends HardwareRenderer { final int surfaceState = checkRenderContextUnsafe(); if (surfaceState != SURFACE_STATE_ERROR) { HardwareCanvas canvas = mCanvas; - attachInfo.mHardwareCanvas = canvas; if (mProfileEnabled) { mProfileLock.lock(); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 5c0be4a..434d473 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -188,6 +188,9 @@ public abstract class HardwareRenderer { */ public static boolean sSystemRendererDisabled = false; + /** @hide */ + public static boolean sUseRenderThread = false; + private boolean mEnabled; private boolean mRequested = true; @@ -306,13 +309,6 @@ public abstract class HardwareRenderer { abstract int getHeight(); /** - * Gets the current canvas associated with this HardwareRenderer. - * - * @return the current HardwareCanvas - */ - abstract HardwareCanvas getCanvas(); - - /** * Outputs extra debugging information in the specified file descriptor. * @param pw */ @@ -335,7 +331,7 @@ public abstract class HardwareRenderer { * * @return True if a property has changed. */ - abstract boolean loadSystemProperties(Surface surface); + abstract boolean loadSystemProperties(); /** * Sets the directory to use as a persistent storage for hardware rendering @@ -412,6 +408,18 @@ public abstract class HardwareRenderer { Rect dirty); /** + * Temporary hook to draw a display list directly, only used if sUseRenderThread + * is true. + * + * @param displayList The display list to draw + * @param attachInfo AttachInfo tied to the specified view. + * @param callbacks Callbacks invoked when drawing happens. + * @param dirty The dirty rectangle to update, can be null. + */ + abstract void drawDisplayList(DisplayList displayList, View.AttachInfo attachInfo, + HardwareDrawCallbacks callbacks, Rect dirty); + + /** * Creates a new hardware layer. A hardware layer built by calling this * method will be treated as a texture layer, instead of as a render target. * @@ -517,10 +525,14 @@ public abstract class HardwareRenderer { * @return A hardware renderer backed by OpenGL. */ static HardwareRenderer create(boolean translucent) { + HardwareRenderer renderer = null; if (GLES20Canvas.isAvailable()) { - return new GLRenderer(translucent); + renderer = new GLRenderer(translucent); + } + if (renderer != null && sUseRenderThread) { + renderer = new ThreadedRenderer((GLRenderer)renderer); } - return null; + return renderer; } /** diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java new file mode 100644 index 0000000..4087313 --- /dev/null +++ b/core/java/android/view/ThreadedRenderer.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Rect; +import android.graphics.SurfaceTexture; +import android.os.Looper; +import android.os.SystemClock; +import android.os.Trace; +import android.util.Log; +import android.view.Surface.OutOfResourcesException; +import android.view.View.AttachInfo; + +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.util.HashMap; + +/** + * Hardware renderer that proxies the rendering to a render thread. Most calls + * are synchronous, however a few such as draw() are posted async. The display list + * is shared between the two threads and is guarded by a top level lock. + * + * The UI thread can block on the RenderThread, but RenderThread must never + * block on the UI thread. + * + * Note that although currently the EGL context & surfaces are created & managed + * by the render thread, the goal is to move that into a shared structure that can + * be managed by both threads. EGLSurface creation & deletion should ideally be + * done on the UI thread and not the RenderThread to avoid stalling the + * RenderThread with surface buffer allocation. + * + * @hide + */ +public class ThreadedRenderer extends HardwareRenderer { + private static final String LOGTAG = "ThreadedRenderer"; + + @SuppressWarnings("serial") + static HashMap<String, Method> sMethodLut = new HashMap<String, Method>() {{ + Method[] methods = HardwareRenderer.class.getDeclaredMethods(); + for (Method m : methods) { + put(m.getName(), m); + } + }}; + static boolean sNeedsInit = true; + + private HardwareRenderer mRemoteRenderer; + private int mWidth, mHeight; + private RTJob mPreviousDraw; + + ThreadedRenderer(GLRenderer backingRenderer) { + mRemoteRenderer = backingRenderer; + setEnabled(true); + if (sNeedsInit) { + sNeedsInit = false; + postToRenderThread(new Runnable() { + @Override + public void run() { + // Hack to allow GLRenderer to create a handler to post the EGL + // destruction to, although it'll never run + Looper.prepare(); + } + }); + } + } + + @Override + void destroy(boolean full) { + run("destroy", full); + } + + @Override + boolean initialize(Surface surface) throws OutOfResourcesException { + return (Boolean) run("initialize", surface); + } + + @Override + void updateSurface(Surface surface) throws OutOfResourcesException { + post("updateSurface", surface); + } + + @Override + void destroyLayers(View view) { + throw new NoSuchMethodError(); + } + + @Override + void destroyHardwareResources(View view) { + run("destroyHardwareResources", view); + } + + @Override + void invalidate(Surface surface) { + post("invalidate", surface); + } + + @Override + boolean validate() { + // TODO Remove users of this API + return false; + } + + @Override + boolean safelyRun(Runnable action) { + return (Boolean) run("safelyRun", action); + } + + @Override + void setup(int width, int height) { + mWidth = width; + mHeight = height; + post("setup", width, height); + } + + @Override + int getWidth() { + return mWidth; + } + + @Override + int getHeight() { + return mHeight; + } + + @Override + void dumpGfxInfo(PrintWriter pw) { + // TODO Auto-generated method stub + } + + @Override + long getFrameCount() { + // TODO Auto-generated method stub + return 0; + } + + @Override + boolean loadSystemProperties() { + return (Boolean) run("loadSystemProperties"); + } + + @Override + void pushLayerUpdate(HardwareLayer layer) { + throw new NoSuchMethodError(); + } + + @Override + void cancelLayerUpdate(HardwareLayer layer) { + throw new NoSuchMethodError(); + } + + @Override + void flushLayerUpdates() { + throw new NoSuchMethodError(); + } + + @Override + void drawDisplayList(DisplayList displayList, AttachInfo attachInfo, + HardwareDrawCallbacks callbacks, Rect dirty) { + throw new NoSuchMethodError(); + } + + /** + * TODO: Remove + * Temporary hack to allow RenderThreadTest prototype app to trigger + * replaying a DisplayList after modifying the displaylist properties + * + * @hide */ + public void repeatLastDraw() { + if (mPreviousDraw == null) { + throw new IllegalStateException("There isn't a previous draw"); + } + synchronized (mPreviousDraw) { + mPreviousDraw.completed = false; + } + mPreviousDraw.args[3] = null; + postToRenderThread(mPreviousDraw); + } + + @Override + void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { + requireCompletion(mPreviousDraw); + + attachInfo.mIgnoreDirtyState = true; + attachInfo.mDrawingTime = SystemClock.uptimeMillis(); + view.mPrivateFlags |= View.PFLAG_DRAWN; + + view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) + == View.PFLAG_INVALIDATED; + view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; + + Trace.traceBegin(Trace.TRACE_TAG_VIEW, "getDisplayList"); + DisplayList displayList = view.getDisplayList(); + Trace.traceEnd(Trace.TRACE_TAG_VIEW); + + view.mRecreateDisplayList = false; + + mPreviousDraw = post("drawDisplayList", displayList, attachInfo, + callbacks, dirty); + } + + @Override + HardwareLayer createHardwareLayer(boolean isOpaque) { + throw new NoSuchMethodError(); + } + + @Override + HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { + throw new NoSuchMethodError(); + } + + @Override + SurfaceTexture createSurfaceTexture(HardwareLayer layer) { + throw new NoSuchMethodError(); + } + + @Override + void setSurfaceTexture(HardwareLayer layer, SurfaceTexture surfaceTexture) { + throw new NoSuchMethodError(); + } + + @Override + void detachFunctor(int functor) { + throw new NoSuchMethodError(); + } + + @Override + boolean attachFunctor(AttachInfo attachInfo, int functor) { + throw new NoSuchMethodError(); + } + + @Override + void setName(String name) { + post("setName", name); + } + + private static void requireCompletion(RTJob job) { + if (job != null) { + synchronized (job) { + if (!job.completed) { + try { + job.wait(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + } + + private RTJob post(String method, Object... args) { + RTJob job = new RTJob(); + job.method = sMethodLut.get(method); + job.args = args; + job.target = mRemoteRenderer; + if (job.method == null) { + throw new NullPointerException("Couldn't find method: " + method); + } + postToRenderThread(job); + return job; + } + + private Object run(String method, Object... args) { + RTJob job = new RTJob(); + job.method = sMethodLut.get(method); + job.args = args; + job.target = mRemoteRenderer; + if (job.method == null) { + throw new NullPointerException("Couldn't find method: " + method); + } + synchronized (job) { + postToRenderThread(job); + try { + job.wait(); + return job.ret; + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + static class RTJob implements Runnable { + Method method; + Object[] args; + Object target; + Object ret; + boolean completed = false; + + @Override + public void run() { + try { + ret = method.invoke(target, args); + synchronized (this) { + completed = true; + notify(); + } + } catch (Exception e) { + Log.e(LOGTAG, "Failed to invoke: " + method.getName(), e); + } + } + } + + /** @hide */ + public static native void postToRenderThread(Runnable runnable); +} diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 5d264b6..05366a7 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11209,6 +11209,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * @hide + */ + public HardwareRenderer getHardwareRenderer() { + return mAttachInfo != null ? mAttachInfo.mHardwareRenderer : null; + } + + /** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * @@ -18843,8 +18850,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final Callbacks mRootCallbacks; - HardwareCanvas mHardwareCanvas; - IWindowId mIWindowId; WindowId mWindowId; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index ff74f9d..cbaf921 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -266,10 +266,10 @@ public final class ViewRootImpl implements ViewParent, int mScrollY; int mCurScrollY; Scroller mScroller; - HardwareLayer mResizeBuffer; - long mResizeBufferStartTime; - int mResizeBufferDuration; - static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); +// HardwareLayer mResizeBuffer; +// long mResizeBufferStartTime; +// int mResizeBufferDuration; +// static final Interpolator mResizeInterpolator = new AccelerateDecelerateInterpolator(); private ArrayList<LayoutTransition> mPendingTransitions; final ViewConfiguration mViewConfiguration; @@ -933,17 +933,17 @@ public final class ViewRootImpl implements ViewParent, return mAppVisible ? mView.getVisibility() : View.GONE; } - void disposeResizeBuffer() { - if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) { - mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() { - @Override - public void run() { - mResizeBuffer.destroy(); - mResizeBuffer = null; - } - }); - } - } +// void disposeResizeBuffer() { +// if (mResizeBuffer != null && mAttachInfo.mHardwareRenderer != null) { +// mAttachInfo.mHardwareRenderer.safelyRun(new Runnable() { +// @Override +// public void run() { +// mResizeBuffer.destroy(); +// mResizeBuffer = null; +// } +// }); +// } +// } /** * Add LayoutTransition to the list of transitions to be started in the next traversal. @@ -1454,75 +1454,76 @@ public final class ViewRootImpl implements ViewParent, final boolean visibleInsetsChanged = !mPendingVisibleInsets.equals( mAttachInfo.mVisibleInsets); if (contentInsetsChanged) { - if (mWidth > 0 && mHeight > 0 && lp != null && - ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility) - & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 && - mSurface != null && mSurface.isValid() && - !mAttachInfo.mTurnOffWindowResizeAnim && - mAttachInfo.mHardwareRenderer != null && - mAttachInfo.mHardwareRenderer.isEnabled() && - mAttachInfo.mHardwareRenderer.validate() && - lp != null && !PixelFormat.formatHasAlpha(lp.format)) { - - disposeResizeBuffer(); - - boolean completed = false; - HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas(); - HardwareCanvas layerCanvas = null; - try { - if (mResizeBuffer == null) { - mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( - mWidth, mHeight, false); - } else if (mResizeBuffer.getWidth() != mWidth || - mResizeBuffer.getHeight() != mHeight) { - mResizeBuffer.resize(mWidth, mHeight); - } - // TODO: should handle create/resize failure - layerCanvas = mResizeBuffer.start(hwRendererCanvas); - final int restoreCount = layerCanvas.save(); - - int yoff; - final boolean scrolling = mScroller != null - && mScroller.computeScrollOffset(); - if (scrolling) { - yoff = mScroller.getCurrY(); - mScroller.abortAnimation(); - } else { - yoff = mScrollY; - } - - layerCanvas.translate(0, -yoff); - if (mTranslator != null) { - mTranslator.translateCanvas(layerCanvas); - } - - DisplayList displayList = mView.mDisplayList; - if (displayList != null && displayList.isValid()) { - layerCanvas.drawDisplayList(displayList, null, - DisplayList.FLAG_CLIP_CHILDREN); - } else { - mView.draw(layerCanvas); - } - - drawAccessibilityFocusedDrawableIfNeeded(layerCanvas); - - mResizeBufferStartTime = SystemClock.uptimeMillis(); - mResizeBufferDuration = mView.getResources().getInteger( - com.android.internal.R.integer.config_mediumAnimTime); - completed = true; - - layerCanvas.restoreToCount(restoreCount); - } catch (OutOfMemoryError e) { - Log.w(TAG, "Not enough memory for content change anim buffer", e); - } finally { - if (mResizeBuffer != null) { - mResizeBuffer.end(hwRendererCanvas); - if (!completed) { - disposeResizeBuffer(); - } - } - } - } +// TODO: Do something with this... +// if (mWidth > 0 && mHeight > 0 && lp != null && +// ((lp.systemUiVisibility|lp.subtreeSystemUiVisibility) +// & View.SYSTEM_UI_LAYOUT_FLAGS) == 0 && +// mSurface != null && mSurface.isValid() && +// !mAttachInfo.mTurnOffWindowResizeAnim && +// mAttachInfo.mHardwareRenderer != null && +// mAttachInfo.mHardwareRenderer.isEnabled() && +// mAttachInfo.mHardwareRenderer.validate() && +// lp != null && !PixelFormat.formatHasAlpha(lp.format)) { +// +// disposeResizeBuffer(); +// +// boolean completed = false; +// HardwareCanvas hwRendererCanvas = mAttachInfo.mHardwareRenderer.getCanvas(); +// HardwareCanvas layerCanvas = null; +// try { +// if (mResizeBuffer == null) { +// mResizeBuffer = mAttachInfo.mHardwareRenderer.createHardwareLayer( +// mWidth, mHeight, false); +// } else if (mResizeBuffer.getWidth() != mWidth || +// mResizeBuffer.getHeight() != mHeight) { +// mResizeBuffer.resize(mWidth, mHeight); +// } +// // TODO: should handle create/resize failure +// layerCanvas = mResizeBuffer.start(hwRendererCanvas); +// final int restoreCount = layerCanvas.save(); +// +// int yoff; +// final boolean scrolling = mScroller != null +// && mScroller.computeScrollOffset(); +// if (scrolling) { +// yoff = mScroller.getCurrY(); +// mScroller.abortAnimation(); +// } else { +// yoff = mScrollY; +// } +// +// layerCanvas.translate(0, -yoff); +// if (mTranslator != null) { +// mTranslator.translateCanvas(layerCanvas); +// } +// +// DisplayList displayList = mView.mDisplayList; +// if (displayList != null && displayList.isValid()) { +// layerCanvas.drawDisplayList(displayList, null, +// DisplayList.FLAG_CLIP_CHILDREN); +// } else { +// mView.draw(layerCanvas); +// } +// +// drawAccessibilityFocusedDrawableIfNeeded(layerCanvas); +// +// mResizeBufferStartTime = SystemClock.uptimeMillis(); +// mResizeBufferDuration = mView.getResources().getInteger( +// com.android.internal.R.integer.config_mediumAnimTime); +// completed = true; +// +// layerCanvas.restoreToCount(restoreCount); +// } catch (OutOfMemoryError e) { +// Log.w(TAG, "Not enough memory for content change anim buffer", e); +// } finally { +// if (mResizeBuffer != null) { +// mResizeBuffer.end(hwRendererCanvas); +// if (!completed) { +// disposeResizeBuffer(); +// } +// } +// } +// } mAttachInfo.mContentInsets.set(mPendingContentInsets); if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: " + mAttachInfo.mContentInsets); @@ -1582,7 +1583,7 @@ public final class ViewRootImpl implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBuffer(); +// disposeResizeBuffer(); // Our surface is gone if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { @@ -2181,23 +2182,28 @@ public final class ViewRootImpl implements ViewParent, @Override public void onHardwarePostDraw(HardwareCanvas canvas) { - if (mResizeBuffer != null) { - mResizePaint.setAlpha(mResizeAlpha); - canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); +// if (mResizeBuffer != null) { +// mResizePaint.setAlpha(mResizeAlpha); +// canvas.drawHardwareLayer(mResizeBuffer, 0.0f, mHardwareYOffset, mResizePaint); +// } + // TODO: this + if (!HardwareRenderer.sUseRenderThread) { + drawAccessibilityFocusedDrawableIfNeeded(canvas); } - drawAccessibilityFocusedDrawableIfNeeded(canvas); } /** * @hide */ void outputDisplayList(View view) { - if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) { - DisplayList displayList = view.getDisplayList(); - if (displayList != null) { - mAttachInfo.mHardwareCanvas.outputDisplayList(displayList); - } - } + // TODO - route through HardwareCanvas so it can be + // proxied to the correct thread +// if (mAttachInfo != null && mAttachInfo.mHardwareCanvas != null) { +// DisplayList displayList = view.getDisplayList(); +// if (displayList != null) { +// mAttachInfo.mHardwareCanvas.outputDisplayList(displayList); +// } +// } } /** @@ -2342,17 +2348,17 @@ public final class ViewRootImpl implements ViewParent, final boolean scalingRequired = attachInfo.mScalingRequired; int resizeAlpha = 0; - if (mResizeBuffer != null) { - long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; - if (deltaTime < mResizeBufferDuration) { - float amt = deltaTime/(float) mResizeBufferDuration; - amt = mResizeInterpolator.getInterpolation(amt); - animating = true; - resizeAlpha = 255 - (int)(amt*255); - } else { - disposeResizeBuffer(); - } - } +// if (mResizeBuffer != null) { +// long deltaTime = SystemClock.uptimeMillis() - mResizeBufferStartTime; +// if (deltaTime < mResizeBufferDuration) { +// float amt = deltaTime/(float) mResizeBufferDuration; +// amt = mResizeInterpolator.getInterpolation(amt); +// animating = true; +// resizeAlpha = 255 - (int)(amt*255); +// } else { +// disposeResizeBuffer(); +// } +// } final Rect dirty = mDirty; if (mSurfaceHolder != null) { @@ -2362,7 +2368,7 @@ public final class ViewRootImpl implements ViewParent, if (mScroller != null) { mScroller.abortAnimation(); } - disposeResizeBuffer(); +// disposeResizeBuffer(); } return; } @@ -2725,7 +2731,7 @@ public final class ViewRootImpl implements ViewParent, if (scrollY != mScrollY) { if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old=" + mScrollY + " , new=" + scrollY); - if (!immediate && mResizeBuffer == null) { + if (!immediate /*&& mResizeBuffer == null*/) { if (mScroller == null) { mScroller = new Scroller(mView.getContext()); } @@ -5404,7 +5410,7 @@ public final class ViewRootImpl implements ViewParent, // Hardware rendering if (mAttachInfo.mHardwareRenderer != null) { - if (mAttachInfo.mHardwareRenderer.loadSystemProperties(mSurface)) { + if (mAttachInfo.mHardwareRenderer.loadSystemProperties()) { invalidate(); } } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index ca519d1..a558909 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -55,6 +55,7 @@ LOCAL_SRC_FILES:= \ android_view_GLRenderer.cpp \ android_view_GLES20DisplayList.cpp \ android_view_GLES20Canvas.cpp \ + android_view_ThreadedRenderer.cpp \ android_view_MotionEvent.cpp \ android_view_PointerIcon.cpp \ android_view_VelocityTracker.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 783e794..b20dc09 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -123,6 +123,7 @@ extern int register_android_view_GraphicBuffer(JNIEnv* env); extern int register_android_view_GLES20DisplayList(JNIEnv* env); extern int register_android_view_GLES20Canvas(JNIEnv* env); extern int register_android_view_GLRenderer(JNIEnv* env); +extern int register_android_view_ThreadedRenderer(JNIEnv* env); extern int register_android_view_Surface(JNIEnv* env); extern int register_android_view_SurfaceControl(JNIEnv* env); extern int register_android_view_SurfaceSession(JNIEnv* env); @@ -1127,6 +1128,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_view_GLES20DisplayList), REG_JNI(register_android_view_GLES20Canvas), REG_JNI(register_android_view_GLRenderer), + REG_JNI(register_android_view_ThreadedRenderer), REG_JNI(register_android_view_Surface), REG_JNI(register_android_view_SurfaceControl), REG_JNI(register_android_view_SurfaceSession), diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp new file mode 100644 index 0000000..8bb6cb4 --- /dev/null +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "GLRenderer" + +#include "jni.h" +#include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> + +#include <renderthread/RenderTask.h> +#include <renderthread/RenderThread.h> + +namespace android { + +#ifdef USE_OPENGL_RENDERER + +namespace RT = android::uirenderer::renderthread; + +static jmethodID gRunnableMethod; + +class JavaTask : public RT::RenderTask { +public: + JavaTask(JNIEnv* env, jobject jrunnable) { + env->GetJavaVM(&mVm); + mRunnable = env->NewGlobalRef(jrunnable); + } + + virtual ~JavaTask() { + env()->DeleteGlobalRef(mRunnable); + } + + virtual void run() { + env()->CallVoidMethod(mRunnable, gRunnableMethod); + }; + +private: + JNIEnv* env() { + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + return 0; + } + return env; + } + + JavaVM* mVm; + jobject mRunnable; +}; + +static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz, + jobject jrunnable) { + RT::RenderTask* task = new JavaTask(env, jrunnable); + RT::RenderThread::getInstance().queue(task); +} + +#endif + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/view/ThreadedRenderer"; + +static JNINativeMethod gMethods[] = { +#ifdef USE_OPENGL_RENDERER + { "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread }, +#endif +}; + +int register_android_view_ThreadedRenderer(JNIEnv* env) { +#ifdef USE_OPENGL_RENDERER + jclass cls = env->FindClass("java/lang/Runnable"); + gRunnableMethod = env->GetMethodID(cls, "run", "()V"); + env->DeleteLocalRef(cls); +#endif + return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +}; // namespace android diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index ce8364e..1f37925 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -45,6 +45,11 @@ ifeq ($(USE_OPENGL_RENDERER),true) TextureCache.cpp \ TextDropShadowCache.cpp + # RenderThread stuff + LOCAL_SRC_FILES += \ + renderthread/RenderTask.cpp \ + renderthread/RenderThread.cpp + intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) LOCAL_C_INCLUDES += \ diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp new file mode 100644 index 0000000..2da91c5 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RenderTask" + +#include "RenderTask.h" + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +RenderTask::RenderTask() : mNext(0) { +} + +RenderTask::~RenderTask() { +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h new file mode 100644 index 0000000..865b1e6 --- /dev/null +++ b/libs/hwui/renderthread/RenderTask.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RENDERTASK_H_ +#define RENDERTASK_H_ + +#include <cutils/compiler.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class ANDROID_API RenderTask { +public: + ANDROID_API RenderTask(); + ANDROID_API virtual ~RenderTask(); + + ANDROID_API virtual void run() = 0; + + RenderTask* mNext; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTASK_H_ */ diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp new file mode 100644 index 0000000..18d9300 --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "RenderThread" + +#include "RenderThread.h" + +#include <utils/Log.h> + +namespace android { +using namespace uirenderer::renderthread; +ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread); + +namespace uirenderer { +namespace renderthread { + +RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() + , mQueueHead(0), mQueueTail(0) { + mLooper = new Looper(false); + run("RenderThread"); +} + +RenderThread::~RenderThread() { +} + +bool RenderThread::threadLoop() { + for (;;) { + int result = mLooper->pollAll(-1); + if (result == ALOOPER_POLL_ERROR) { + // TODO Something? + break; + } + // Process our queue, if we have anything + while (RenderTask* task = nextTask()) { + task->run(); + delete task; + } + } + + return false; +} + +void RenderThread::queue(RenderTask* task) { + AutoMutex _lock(mLock); + if (mQueueTail) { + mQueueTail->mNext = task; + } else { + mQueueHead = task; + } + mQueueTail = task; + if (mQueueHead == task) { + // Only wake if this is the first task + mLooper->wake(); + } +} + +RenderTask* RenderThread::nextTask() { + AutoMutex _lock(mLock); + RenderTask* ret = mQueueHead; + if (ret) { + if (mQueueTail == mQueueHead) { + mQueueTail = mQueueHead = 0; + } else { + mQueueHead = ret->mNext; + } + ret->mNext = 0; + } + return ret; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h new file mode 100644 index 0000000..4edd575 --- /dev/null +++ b/libs/hwui/renderthread/RenderThread.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RENDERTHREAD_H_ +#define RENDERTHREAD_H_ + +#include "RenderTask.h" +#include <cutils/compiler.h> +#include <utils/Looper.h> +#include <utils/Mutex.h> +#include <utils/Singleton.h> +#include <utils/Thread.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +public: + // RenderThread takes complete ownership of tasks that are queued + // and will delete them after they are run + ANDROID_API void queue(RenderTask* task); + +protected: + virtual bool threadLoop(); + +private: + friend class Singleton<RenderThread>; + + RenderThread(); + virtual ~RenderThread(); + + RenderTask* nextTask(); + + sp<Looper> mLooper; + Mutex mLock; + + RenderTask* mQueueHead; + RenderTask* mQueueTail; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ +#endif /* RENDERTHREAD_H_ */ diff --git a/tests/RenderThreadTest/Android.mk b/tests/RenderThreadTest/Android.mk new file mode 100644 index 0000000..bdcba2e --- /dev/null +++ b/tests/RenderThreadTest/Android.mk @@ -0,0 +1,18 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := optional + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := RenderThreadTest + +LOCAL_STATIC_JAVA_LIBRARIES += android-common + +LOCAL_PROGUARD_ENABLED := disabled + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/tests/RenderThreadTest/AndroidManifest.xml b/tests/RenderThreadTest/AndroidManifest.xml new file mode 100644 index 0000000..c76cfce --- /dev/null +++ b/tests/RenderThreadTest/AndroidManifest.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.renderthread" + android:versionCode="1" + android:versionName="1.0" > + + <uses-sdk + android:minSdkVersion="18" + android:targetSdkVersion="18" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" > + <activity + android:name=".MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + <activity android:name=".SubActivity" + android:theme="@style/AppTheme.Transparent" /> + </application> + +</manifest> diff --git a/tests/RenderThreadTest/res/drawable-hdpi/ic_launcher.png b/tests/RenderThreadTest/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..96a442e --- /dev/null +++ b/tests/RenderThreadTest/res/drawable-hdpi/ic_launcher.png diff --git a/tests/RenderThreadTest/res/drawable-mdpi/ic_launcher.png b/tests/RenderThreadTest/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..359047d --- /dev/null +++ b/tests/RenderThreadTest/res/drawable-mdpi/ic_launcher.png diff --git a/tests/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png b/tests/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..71c6d76 --- /dev/null +++ b/tests/RenderThreadTest/res/drawable-xhdpi/ic_launcher.png diff --git a/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg b/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg Binary files differnew file mode 100644 index 0000000..755232d --- /dev/null +++ b/tests/RenderThreadTest/res/drawable-xhdpi/starry_night_bg.jpg diff --git a/tests/RenderThreadTest/res/layout/activity_main.xml b/tests/RenderThreadTest/res/layout/activity_main.xml new file mode 100644 index 0000000..1fd5459 --- /dev/null +++ b/tests/RenderThreadTest/res/layout/activity_main.xml @@ -0,0 +1,12 @@ +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".MainActivity" > + + <ListView android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:divider="@null" /> + +</FrameLayout> diff --git a/tests/RenderThreadTest/res/layout/activity_sub.xml b/tests/RenderThreadTest/res/layout/activity_sub.xml new file mode 100644 index 0000000..713cee4 --- /dev/null +++ b/tests/RenderThreadTest/res/layout/activity_sub.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" > + + <View + android:id="@+id/bg_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/starry_night_bg" /> + + <LinearLayout + android:id="@+id/my_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" > + + <View + android:id="@+id/from_left" + android:layout_width="match_parent" + android:layout_height="48dip" + android:background="#7000FF00" /> + + <View + android:id="@+id/from_right" + android:layout_width="match_parent" + android:layout_height="0dip" + android:layout_margin="80dip" + android:layout_weight="1" + android:background="#90FF0000" /> + + <View + android:id="@+id/from_left" + android:layout_width="match_parent" + android:layout_height="48dip" + android:background="#7000FF00" /> + </LinearLayout> + +</FrameLayout>
\ No newline at end of file diff --git a/tests/RenderThreadTest/res/layout/item_layout.xml b/tests/RenderThreadTest/res/layout/item_layout.xml new file mode 100644 index 0000000..5bdb1ac --- /dev/null +++ b/tests/RenderThreadTest/res/layout/item_layout.xml @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="utf-8"?> +<TextView xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@android:id/text1" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceListItemSmall" + android:gravity="center_vertical" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:minHeight="?android:attr/listPreferredItemHeightSmall" + android:background="#33b5e5" +/>
\ No newline at end of file diff --git a/tests/RenderThreadTest/res/values/strings.xml b/tests/RenderThreadTest/res/values/strings.xml new file mode 100644 index 0000000..f782e98 --- /dev/null +++ b/tests/RenderThreadTest/res/values/strings.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + + <string name="app_name">Render Thread</string> + <string name="action_settings">Settings</string> + <string name="hello_world">Hello world!</string> + +</resources> diff --git a/tests/RenderThreadTest/res/values/styles.xml b/tests/RenderThreadTest/res/values/styles.xml new file mode 100644 index 0000000..f6b5d6a --- /dev/null +++ b/tests/RenderThreadTest/res/values/styles.xml @@ -0,0 +1,11 @@ +<resources> + <!-- Application theme. --> + <style name="AppTheme" parent="android:Theme.Holo.Light"> + </style> + + <style name="AppTheme.Transparent"> + <item name="android:windowIsTranslucent">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + </style> + +</resources> diff --git a/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java new file mode 100644 index 0000000..a39aba8 --- /dev/null +++ b/tests/RenderThreadTest/src/com/example/renderthread/MainActivity.java @@ -0,0 +1,158 @@ + +package com.example.renderthread; + +import android.animation.TimeInterpolator; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.SystemClock; +import android.view.DisplayList; +import android.view.HardwareRenderer; +import android.view.ThreadedRenderer; +import android.view.View; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ListView; +import android.widget.SimpleAdapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +public class MainActivity extends Activity implements OnItemClickListener { + + static final int DURATION = 400; + + static final String KEY_NAME = "name"; + static final String KEY_CLASS = "clazz"; + + static Map<String,?> make(String name) { + Map<String,Object> ret = new HashMap<String,Object>(); + ret.put(KEY_NAME, name); + return ret; + } + + @SuppressWarnings("serial") + static final ArrayList<Map<String,?>> SAMPLES = new ArrayList<Map<String,?>>() {{ + for (int i = 1; i < 25; i++) { + add(make("List Item: " + i)); + } + }}; + + Handler mHandler = new Handler(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + HardwareRenderer.sUseRenderThread = true; + setContentView(R.layout.activity_main); + ListView lv = (ListView) findViewById(android.R.id.list); + lv.setDrawSelectorOnTop(true); + lv.setAdapter(new SimpleAdapter(this, SAMPLES, + R.layout.item_layout, new String[] { KEY_NAME }, + new int[] { android.R.id.text1 })); + lv.setOnItemClickListener(this); + getActionBar().setTitle("MainActivity"); + } + + @Override + protected void onResume() { + super.onResume(); + ListView lv = (ListView) findViewById(android.R.id.list); + for (int i = 0; i < lv.getChildCount(); i++) { + lv.getChildAt(i).animate().translationY(0).setDuration(DURATION); + } + } + + private static class DisplayListAnimator { + private static final TimeInterpolator sDefaultInterpolator = + new AccelerateDecelerateInterpolator(); + + DisplayList mDisplayList; + float mFromValue; + float mDelta; + long mDuration = DURATION * 2; + long mStartTime; + + DisplayListAnimator(View view, float translateXBy) { + mDelta = translateXBy; + mFromValue = view.getTranslationY(); + mDisplayList = view.getDisplayList(); + } + + boolean animate(long currentTime) { + if (mStartTime == 0) mStartTime = currentTime; + + float fraction = (float)(currentTime - mStartTime) / mDuration; + if (fraction > 1) { + return false; + } + fraction = sDefaultInterpolator.getInterpolation(fraction); + float translation = mFromValue + (mDelta * fraction); + mDisplayList.setTranslationY(translation); + return fraction < 1f; + } + } + + private static class AnimationExecutor implements Runnable { + DisplayListAnimator[] mAnimations; + ThreadedRenderer mRenderer; + + AnimationExecutor(ThreadedRenderer renderer, DisplayListAnimator[] animations) { + mRenderer = renderer; + mAnimations = animations; + ThreadedRenderer.postToRenderThread(this); + } + + @Override + public void run() { + boolean hasMore = false; + long now = SystemClock.uptimeMillis(); + for (DisplayListAnimator animator : mAnimations) { + hasMore |= animator.animate(now); + } + mRenderer.repeatLastDraw(); + if (hasMore) { + ThreadedRenderer.postToRenderThread(this); + } + } + + } + + @Override + public void onItemClick(final AdapterView<?> adapterView, View clickedView, + int clickedPosition, long clickedId) { + int topPosition = adapterView.getFirstVisiblePosition(); + int dy = adapterView.getHeight(); + final DisplayListAnimator[] animators = new DisplayListAnimator[adapterView.getChildCount()]; + for (int i = 0; i < adapterView.getChildCount(); i++) { + int pos = topPosition + i; + View child = adapterView.getChildAt(i); + float delta = (pos - clickedPosition) * 1.1f; + if (delta == 0) delta = -1; + animators[i] = new DisplayListAnimator(child, dy * delta); + } + adapterView.invalidate(); + adapterView.post(new Runnable() { + + @Override + public void run() { + new AnimationExecutor((ThreadedRenderer) adapterView.getHardwareRenderer(), animators); + } + }); + //mHandler.postDelayed(mLaunchActivity, (long) (DURATION * .4)); + mLaunchActivity.run(); + } + + private Runnable mLaunchActivity = new Runnable() { + + @Override + public void run() { + startActivity(new Intent(MainActivity.this, SubActivity.class)); + overridePendingTransition(0, 0); + } + }; + +} diff --git a/tests/RenderThreadTest/src/com/example/renderthread/SubActivity.java b/tests/RenderThreadTest/src/com/example/renderthread/SubActivity.java new file mode 100644 index 0000000..892cbae --- /dev/null +++ b/tests/RenderThreadTest/src/com/example/renderthread/SubActivity.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example.renderthread; + +import android.app.Activity; +import android.os.Bundle; +import android.os.Process; +import android.os.SystemClock; +import android.view.View; +import android.view.ViewGroup; + +public class SubActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + long before = SystemClock.currentThreadTimeMillis(); + setContentView(R.layout.activity_sub); + getActionBar().setTitle("SubActivity"); + // Simulate being a real app! + while (SystemClock.currentThreadTimeMillis() - before < 100) { + View v = new View(this, null); + } + } + + @Override + protected void onResume() { + super.onResume(); + ViewGroup container = (ViewGroup) findViewById(R.id.my_container); + int dx = getWindowManager().getDefaultDisplay().getWidth(); + for (int i = 0; i < container.getChildCount(); i++) { + View child = container.getChildAt(i); + int dir = child.getId() == R.id.from_left ? 1 : -1; + child.setTranslationX(dx * dir); + child.animate().translationX(0).setDuration(MainActivity.DURATION); + } + View bg = findViewById(R.id.bg_container); + bg.setAlpha(0f); + bg.animate().alpha(1f).setDuration(MainActivity.DURATION); + } + + @Override + public void onBackPressed() { + super.onBackPressed(); + overridePendingTransition(0, 0); + } +} |