diff options
-rw-r--r-- | core/java/android/view/DisplayList.java | 55 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 71 | ||||
-rw-r--r-- | core/java/android/view/GLES20DisplayList.java | 74 | ||||
-rw-r--r-- | core/java/android/view/HardwareCanvas.java | 60 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 15 | ||||
-rw-r--r-- | core/java/android/view/View.java | 101 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 33 | ||||
-rw-r--r-- | core/java/android/view/ViewRoot.java | 35 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 31 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 300 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.h | 144 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 6 |
12 files changed, 798 insertions, 127 deletions
diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java new file mode 100644 index 0000000..b1160f0 --- /dev/null +++ b/core/java/android/view/DisplayList.java @@ -0,0 +1,55 @@ +/* + * 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. + */ + +package android.view; + +/** + * A display lists records a series of graphics related operation and can replay + * them later. Display lists are usually built by recording operations on a + * {@link android.graphics.Canvas}. Replaying the operations from a display list + * avoids executing views drawing code on every frame, and is thus much more + * efficient. + */ +abstract class DisplayList { + /** + * Starts recording the display list. All operations performed on the + * returned canvas are recorded and stored in this display list. + * + * @return A canvas to record drawing operations. + */ + abstract HardwareCanvas start(); + + /** + * Ends the recording for this display list. A display list cannot be + * replayed if recording is not finished. + */ + abstract void end(); + + /** + * Frees resources taken by this display list. This method must be called + * before releasing all references. + */ + abstract void destroy(); + + /** + * Indicates whether this display list can be replayed or not. + * + * @return True if the display list can be replayed, false otherwise. + * + * @see android.view.HardwareCanvas#drawDisplayList(DisplayList) + */ + abstract boolean isReady(); +} diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index f917001..f0b00dd 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -35,14 +35,10 @@ import android.text.SpannableString; import android.text.SpannedString; import android.text.TextUtils; -import javax.microedition.khronos.opengles.GL; - /** * An implementation of Canvas on top of OpenGL ES 2.0. */ -class GLES20Canvas extends Canvas { - @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) - private final GL mGl; +class GLES20Canvas extends HardwareCanvas { private final boolean mOpaque; private int mRenderer; @@ -72,27 +68,29 @@ class GLES20Canvas extends Canvas { /////////////////////////////////////////////////////////////////////////// // Constructors /////////////////////////////////////////////////////////////////////////// + + GLES20Canvas(boolean translucent) { + this(false, translucent); + } - GLES20Canvas(GL gl, boolean translucent) { - mGl = gl; + GLES20Canvas(boolean record, boolean translucent) { mOpaque = !translucent; - mRenderer = nCreateRenderer(); + if (record) { + mRenderer = nCreateDisplayListRenderer(); + } else { + mRenderer = nCreateRenderer(); + } + if (mRenderer == 0) { throw new IllegalStateException("Could not create GLES20Canvas renderer"); } } - - private native int nCreateRenderer(); - - /** - * This method <strong>must</strong> be called before releasing a - * reference to a GLES20Canvas. This method is responsible for freeing - * native resources associated with the hardware. Not invoking this - * method properly can result in memory leaks. - * - * @hide - */ + + private native int nCreateRenderer(); + private native int nCreateDisplayListRenderer(); + + @Override public synchronized void destroy() { if (mRenderer != 0) { nDestroyRenderer(mRenderer); @@ -105,16 +103,6 @@ class GLES20Canvas extends Canvas { /////////////////////////////////////////////////////////////////////////// // Canvas management /////////////////////////////////////////////////////////////////////////// - - @Override - public boolean isHardwareAccelerated() { - return true; - } - - @Override - public void setBitmap(Bitmap bitmap) { - throw new UnsupportedOperationException(); - } @Override public boolean isOpaque() { @@ -145,12 +133,14 @@ class GLES20Canvas extends Canvas { private native void nSetViewport(int renderer, int width, int height); + @Override void onPreDraw() { nPrepare(mRenderer); } private native void nPrepare(int renderer); + @Override void onPostDraw() { nFinish(mRenderer); } @@ -177,6 +167,29 @@ class GLES20Canvas extends Canvas { } private native void nReleaseContext(int renderer); + + /////////////////////////////////////////////////////////////////////////// + // Display list + /////////////////////////////////////////////////////////////////////////// + + int getDisplayList() { + return nCreateDisplayList(mRenderer); + } + + private native int nCreateDisplayList(int renderer); + + void destroyDisplayList(int displayList) { + nDestroyDisplayList(displayList); + } + + private native void nDestroyDisplayList(int displayList); + + @Override + public void drawDisplayList(DisplayList displayList) { + nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).mNativeDisplayList); + } + + private native void nDrawDisplayList(int renderer, int displayList); /////////////////////////////////////////////////////////////////////////// // Clipping diff --git a/core/java/android/view/GLES20DisplayList.java b/core/java/android/view/GLES20DisplayList.java new file mode 100644 index 0000000..2886bf3 --- /dev/null +++ b/core/java/android/view/GLES20DisplayList.java @@ -0,0 +1,74 @@ +/* + * 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. + */ + +package android.view; + +/** + * An implementation of display list for OpenGL ES 2.0. + */ +class GLES20DisplayList extends DisplayList { + private GLES20Canvas mCanvas; + + private boolean mStarted = false; + private boolean mRecorded = false; + + int mNativeDisplayList; + + @Override + HardwareCanvas start() { + if (mStarted) { + throw new IllegalStateException("Recording has already started"); + } + + destroyCanvas(); + + mCanvas = new GLES20Canvas(true, true); + mStarted = true; + mRecorded = false; + + return mCanvas; + } + + private void destroyCanvas() { + if (mCanvas != null) { + mCanvas.destroyDisplayList(mNativeDisplayList); + mCanvas.destroy(); + + mCanvas = null; + mNativeDisplayList = 0; + } + } + + @Override + void end() { + if (mCanvas != null) { + mStarted = false; + mRecorded = true; + + mNativeDisplayList = mCanvas.getDisplayList(); + } + } + + @Override + void destroy() { + destroyCanvas(); + } + + @Override + boolean isReady() { + return !mStarted && mRecorded; + } +} diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java new file mode 100644 index 0000000..22d2fe6 --- /dev/null +++ b/core/java/android/view/HardwareCanvas.java @@ -0,0 +1,60 @@ +/* + * 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. + */ + +package android.view; + +import android.graphics.Bitmap; +import android.graphics.Canvas; + +/** + * Hardware accelerated canvas. + */ +abstract class HardwareCanvas extends Canvas { + @Override + public boolean isHardwareAccelerated() { + return true; + } + + @Override + public void setBitmap(Bitmap bitmap) { + throw new UnsupportedOperationException(); + } + + /** + * This method <strong>must</strong> be called before releasing a + * reference to a hardware canvas. This method is responsible for + * freeing native resources associated with the hardware. Not + * invoking this method properly can result in memory leaks. + */ + public abstract void destroy(); + + /** + * Invoked before any drawing operation is performed in this canvas. + */ + abstract void onPreDraw(); + + /** + * Invoked after all drawing operation have been performed. + */ + abstract void onPostDraw(); + + /** + * Draws the specified display list onto this canvas. + * + * @param displayList The display list to replay. + */ + public abstract void drawDisplayList(DisplayList displayList); +} diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index fbb13af..2cc4052 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -103,6 +103,14 @@ public abstract class HardwareRenderer { abstract void draw(View view, View.AttachInfo attachInfo, int yOffset); /** + * Creates a new canvas that can be used to record drawing operations + * in the specified display list. + * + * @return A new recording canvas. + */ + abstract DisplayList createDisplayList(); + + /** * Initializes the hardware renderer for the specified surface and setup the * renderer for drawing, if needed. This is invoked when the ViewRoot has * potentially lost the hardware renderer. The hardware renderer should be @@ -577,7 +585,7 @@ public abstract class HardwareRenderer { @Override GLES20Canvas createCanvas() { - return mGlCanvas = new GLES20Canvas(mGl, true); + return mGlCanvas = new GLES20Canvas(true); } @Override @@ -590,6 +598,11 @@ public abstract class HardwareRenderer { mGlCanvas.onPostDraw(); } + @Override + DisplayList createDisplayList() { + return new GLES20DisplayList(); + } + static HardwareRenderer create(boolean translucent) { if (GLES20Canvas.isAvailable()) { return new Gl20Renderer(translucent); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 96066b7..3b10437 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1250,12 +1250,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility */ private static final int[][] VIEW_STATE_SETS; - static final int VIEW_STATE_WINDOW_FOCUSED = 1<<0; - static final int VIEW_STATE_SELECTED = 1<<1; - static final int VIEW_STATE_FOCUSED = 1<<2; - static final int VIEW_STATE_ENABLED = 1<<3; - static final int VIEW_STATE_PRESSED = 1<<4; - static final int VIEW_STATE_ACTIVATED = 1<<5; + static final int VIEW_STATE_WINDOW_FOCUSED = 1; + static final int VIEW_STATE_SELECTED = 1 << 1; + static final int VIEW_STATE_FOCUSED = 1 << 2; + static final int VIEW_STATE_ENABLED = 1 << 3; + static final int VIEW_STATE_PRESSED = 1 << 4; + static final int VIEW_STATE_ACTIVATED = 1 << 5; static final int[] VIEW_STATE_IDS = new int[] { R.attr.state_window_focused, VIEW_STATE_WINDOW_FOCUSED, @@ -1268,28 +1268,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility static { int[] orderedIds = new int[VIEW_STATE_IDS.length]; - for (int i=0; i<R.styleable.ViewDrawableStates.length; i++) { + for (int i = 0; i < R.styleable.ViewDrawableStates.length; i++) { int viewState = R.styleable.ViewDrawableStates[i]; - for (int j=0; j<VIEW_STATE_IDS.length; j+=2) { + for (int j = 0; j<VIEW_STATE_IDS.length; j += 2) { if (VIEW_STATE_IDS[j] == viewState) { - orderedIds[i*2] = viewState; - orderedIds[i*2+1] = VIEW_STATE_IDS[j+1]; + orderedIds[i * 2] = viewState; + orderedIds[i * 2 + 1] = VIEW_STATE_IDS[j + 1]; } } } - final int NUM_BITS = VIEW_STATE_IDS.length/2; - VIEW_STATE_SETS = new int[1<<NUM_BITS][]; - for (int i=0; i<VIEW_STATE_SETS.length; i++) { + final int NUM_BITS = VIEW_STATE_IDS.length / 2; + VIEW_STATE_SETS = new int[1 << NUM_BITS][]; + for (int i = 0; i < VIEW_STATE_SETS.length; i++) { int numBits = Integer.bitCount(i); int[] set = new int[numBits]; int pos = 0; - for (int j=0; j<orderedIds.length; j+=2) { - if ((i&orderedIds[j+1]) != 0) { - if (false) { - Log.i("View", "Index #" + i + " @ ordered #" + j - + " resid=0x" + Integer.toHexString(orderedIds[j]) - + " mask " + orderedIds[j+1]); - } + for (int j = 0; j < orderedIds.length; j += 2) { + if ((i & orderedIds[j+1]) != 0) { set[pos++] = orderedIds[j]; } } @@ -1958,6 +1953,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private Bitmap mDrawingCache; private Bitmap mUnscaledDrawingCache; + private DisplayList mDisplayList; /** * When this view has focus and the next focus is {@link #FOCUS_LEFT}, @@ -7317,6 +7313,64 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * <p>Returns a display list that can be used to draw this view again + * without executing its draw method.</p> + * + * @return A DisplayList ready to replay, or null if caching is not enabled. + */ + DisplayList getDisplayList() { + if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) { + return null; + } + + if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { + return null; + } + + if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED && + ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDisplayList == null)) { + + if (mDisplayList != null) { + mDisplayList.destroy(); + } + + mDisplayList = mAttachInfo.mHardwareRenderer.createDisplayList(); + + final HardwareCanvas canvas = mDisplayList.start(); + try { + int width = mRight - mLeft; + int height = mBottom - mTop; + + canvas.setViewport(width, height); + canvas.onPreDraw(); + + final int restoreCount = canvas.save(); + + mPrivateFlags |= DRAWN; + mPrivateFlags |= DRAWING_CACHE_VALID; + + // Fast path for layouts with no backgrounds + if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { + mPrivateFlags &= ~DIRTY_MASK; + dispatchDraw(canvas); + } else { + draw(canvas); + } + + canvas.restoreToCount(restoreCount); + } finally { + canvas.onPostDraw(); + + mDisplayList.end(); + + canvas.destroy(); + } + } + + return mDisplayList; + } + + /** * <p>Calling this method is equivalent to calling <code>getDrawingCache(false)</code>.</p> * * @return A non-scaled bitmap representing this view or null if cache is disabled. @@ -7383,6 +7437,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mUnscaledDrawingCache.recycle(); mUnscaledDrawingCache = null; } + if (mDisplayList != null) { + mDisplayList.destroy(); + mDisplayList = null; + } } /** @@ -10167,7 +10225,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility IBinder mPanelParentWindowToken; Surface mSurface; - boolean mHardwareAccelerated; + boolean mHardwareAccelerated; + HardwareRenderer mHardwareRenderer; /** * Scale factor used by the compatibility mode diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 12c49c4..570e288 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1830,8 +1830,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager boolean scalingRequired = false; boolean caching = false; - if (!canvas.isHardwareAccelerated() && - (flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || + if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE || (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) { caching = true; if (mAttachInfo != null) scalingRequired = mAttachInfo.mScalingRequired; @@ -1914,12 +1913,18 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager final int sx = child.mScrollX; final int sy = child.mScrollY; + DisplayList displayList = null; Bitmap cache = null; if (caching) { - cache = child.getDrawingCache(true); + if (!canvas.isHardwareAccelerated()) { + cache = child.getDrawingCache(true); + } else { + displayList = child.getDisplayList(); + } } - final boolean hasNoCache = cache == null; + final boolean hasDisplayList = displayList != null && displayList.isReady(); + final boolean hasNoCache = cache == null || hasDisplayList; final int restoreTo = canvas.save(); if (hasNoCache) { @@ -2002,17 +2007,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } if (hasNoCache) { - // Fast path for layouts with no backgrounds - if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { - if (ViewDebug.TRACE_HIERARCHY) { - ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); + if (!hasDisplayList) { + // Fast path for layouts with no backgrounds + if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) { + if (ViewDebug.TRACE_HIERARCHY) { + ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW); + } + child.mPrivateFlags &= ~DIRTY_MASK; + child.dispatchDraw(canvas); + } else { + child.draw(canvas); } - child.mPrivateFlags &= ~DIRTY_MASK; - child.dispatchDraw(canvas); } else { - child.draw(canvas); + ((HardwareCanvas) canvas).drawDisplayList(displayList); } - } else { + } else if (cache != null) { final Paint cachePaint = mCachePaint; if (alpha < 1.0f) { cachePaint.setAlpha((int) (alpha * 255)); diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index f6a06ce..77ba6fe 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -196,8 +196,6 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn int mCurScrollY; Scroller mScroller; - HardwareRenderer mHwRenderer; - final ViewConfiguration mViewConfiguration; /** @@ -451,10 +449,10 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (attrs != null && (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0) { final boolean translucent = attrs.format != PixelFormat.OPAQUE; - if (mHwRenderer != null) { - mHwRenderer.destroy(true); + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(true); } - mHwRenderer = HardwareRenderer.createGlRenderer(2, translucent); + mAttachInfo.mHardwareRenderer = HardwareRenderer.createGlRenderer(2, translucent); mAttachInfo.mHardwareAccelerated = true; } } @@ -663,8 +661,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn attachInfo.mWindowVisibility = viewVisibility; host.dispatchWindowVisibilityChanged(viewVisibility); if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) { - if (mHwRenderer != null) { - mHwRenderer.destroy(false); + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(false); } } if (viewVisibility == View.GONE) { @@ -869,8 +867,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn fullRedrawNeeded = true; mPreviousTransparentRegion.setEmpty(); - if (mHwRenderer != null) { - hwIntialized = mHwRenderer.initialize(mHolder); + if (mAttachInfo.mHardwareRenderer != null) { + hwIntialized = mAttachInfo.mHardwareRenderer.initialize(mHolder); } } } else if (!mSurface.isValid()) { @@ -948,8 +946,8 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } } - if (hwIntialized || (windowShouldResize && mHwRenderer != null)) { - mHwRenderer.setup(mWidth, mHeight); + if (hwIntialized || (windowShouldResize && mAttachInfo.mHardwareRenderer != null)) { + mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight); } boolean focusChangedDueToTouchMode = ensureTouchModeLocally( @@ -1262,9 +1260,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn dirty.union(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f)); } - if (mHwRenderer != null && mHwRenderer.isEnabled()) { + if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) { if (!dirty.isEmpty()) { - mHwRenderer.draw(mView, mAttachInfo, yoff); + mAttachInfo.mHardwareRenderer.draw(mView, mAttachInfo, yoff); } if (scrolling) { @@ -1774,8 +1772,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn boolean inTouchMode = msg.arg2 != 0; ensureTouchModeLocally(inTouchMode); - if (mHwRenderer != null) { - mHwRenderer.initializeIfNeeded(mWidth, mHeight, mAttachInfo, mHolder); + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, + mAttachInfo, mHolder); } } @@ -2582,9 +2581,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } private void destroyHardwareRenderer() { - if (mHwRenderer != null) { - mHwRenderer.destroy(true); - mHwRenderer = null; + if (mAttachInfo.mHardwareRenderer != null) { + mAttachInfo.mHardwareRenderer.destroy(true); + mAttachInfo.mHardwareRenderer = null; mAttachInfo.mHardwareAccelerated = false; } } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index bbf3509..cb1556b 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -30,6 +30,7 @@ #include <SkTemplates.h> #include <SkXfermode.h> +#include <DisplayListRenderer.h> #include <OpenGLDebugRenderer.h> #include <OpenGLRenderer.h> #include <SkiaShader.h> @@ -378,6 +379,30 @@ static void android_view_GLES20Canvas_drawTextRun(JNIEnv* env, jobject canvas, env->ReleaseStringChars(text, textArray); } +// ---------------------------------------------------------------------------- +// Display lists +// ---------------------------------------------------------------------------- + +static OpenGLRenderer* android_view_GLES20Canvas_createDisplayListRenderer( + JNIEnv* env, jobject canvas) { + return new DisplayListRenderer; +} + +static DisplayList* android_view_GLES20Canvas_createDisplayList(JNIEnv* env, + jobject canvas, DisplayListRenderer* renderer) { + return renderer->getDisplayList(); +} + +static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, + jobject canvas, DisplayList* displayList) { + delete displayList; +} + +static void android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, + jobject canvas, OpenGLRenderer* renderer, DisplayList* displayList) { + displayList->replay(*renderer); +} + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -455,6 +480,12 @@ static JNINativeMethod gMethods[] = { { "nGetClipBounds", "(ILandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds }, + + { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, + { "nCreateDisplayList", "(I)I", (void*) android_view_GLES20Canvas_createDisplayList }, + { "nDestroyDisplayList", "(I)V", (void*) android_view_GLES20Canvas_destroyDisplayList }, + { "nDrawDisplayList", "(II)V", (void*) android_view_GLES20Canvas_drawDisplayList }, + #endif }; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 16b6b56..ee90702 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -22,6 +22,229 @@ namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// +// Display list +/////////////////////////////////////////////////////////////////////////////// + +DisplayList::DisplayList(const DisplayListRenderer& recorder) { + const SkWriter32& writer = recorder.writeStream(); + init(); + + if (writer.size() == 0) { + return; + } + + size_t size = writer.size(); + void* buffer = sk_malloc_throw(size); + writer.flatten(buffer); + mReader.setMemory(buffer, size); + + mRCPlayback.reset(&recorder.mRCRecorder); + mRCPlayback.setupBuffer(mReader); + + mTFPlayback.reset(&recorder.mTFRecorder); + mTFPlayback.setupBuffer(mReader); + + const SkTDArray<const SkFlatBitmap*>& bitmaps = recorder.getBitmaps(); + mBitmapCount = bitmaps.count(); + if (mBitmapCount > 0) { + mBitmaps = new SkBitmap[mBitmapCount]; + for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin(); + flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) { + const SkFlatBitmap* flatBitmap = *flatBitmapPtr; + int index = flatBitmap->index() - 1; + flatBitmap->unflatten(&mBitmaps[index], &mRCPlayback); + } + } + + const SkTDArray<const SkFlatMatrix*>& matrices = recorder.getMatrices(); + mMatrixCount = matrices.count(); + if (mMatrixCount > 0) { + mMatrices = new SkMatrix[mMatrixCount]; + for (const SkFlatMatrix** matrixPtr = matrices.begin(); + matrixPtr != matrices.end(); matrixPtr++) { + const SkFlatMatrix* flatMatrix = *matrixPtr; + flatMatrix->unflatten(&mMatrices[flatMatrix->index() - 1]); + } + } + + const SkTDArray<const SkFlatPaint*>& paints = recorder.getPaints(); + mPaintCount = paints.count(); + if (mPaintCount > 0) { + mPaints = new SkPaint[mPaintCount]; + for (const SkFlatPaint** flatPaintPtr = paints.begin(); + flatPaintPtr != paints.end(); flatPaintPtr++) { + const SkFlatPaint* flatPaint = *flatPaintPtr; + int index = flatPaint->index() - 1; + flatPaint->unflatten(&mPaints[index], &mRCPlayback, &mTFPlayback); + } + } + + mPathHeap = recorder.mPathHeap; + mPathHeap->safeRef(); +} + +DisplayList::~DisplayList() { + sk_free((void*) mReader.base()); + + Caches& caches = Caches::getInstance(); + for (int i = 0; i < mBitmapCount; i++) { + caches.textureCache.remove(&mBitmaps[i]); + } + + delete[] mBitmaps; + delete[] mMatrices; + delete[] mPaints; + + mPathHeap->safeUnref(); +} + +void DisplayList::init() { + mBitmaps = NULL; + mMatrices = NULL; + mPaints = NULL; + mPathHeap = NULL; + mBitmapCount = mMatrixCount = mPaintCount = 0; +} + +void DisplayList::replay(OpenGLRenderer& renderer) { + TextContainer text; + mReader.rewind(); + + int saveCount = renderer.getSaveCount() - 1; + + while (!mReader.eof()) { + switch (mReader.readInt()) { + case AcquireContext: { + renderer.acquireContext(); + } + break; + case ReleaseContext: { + renderer.releaseContext(); + } + break; + case Save: { + renderer.save(getInt()); + } + break; + case Restore: { + renderer.restore(); + } + break; + case RestoreToCount: { + renderer.restoreToCount(saveCount + getInt()); + } + break; + case SaveLayer: { + renderer.saveLayer(getFloat(), getFloat(), getFloat(), getFloat(), + getPaint(), getInt()); + } + break; + case Translate: { + renderer.translate(getFloat(), getFloat()); + } + break; + case Rotate: { + renderer.rotate(getFloat()); + } + break; + case Scale: { + renderer.scale(getFloat(), getFloat()); + } + break; + case SetMatrix: { + renderer.setMatrix(getMatrix()); + } + break; + case ConcatMatrix: { + renderer.concatMatrix(getMatrix()); + } + break; + case ClipRect: { + renderer.clipRect(getFloat(), getFloat(), getFloat(), getFloat(), + (SkRegion::Op) getInt()); + } + break; + case DrawBitmap: { + renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawBitmapMatrix: { + renderer.drawBitmap(getBitmap(), getMatrix(), getPaint()); + } + break; + case DrawBitmapRect: { + renderer.drawBitmap(getBitmap(), getFloat(), getFloat(), getFloat(), getFloat(), + getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawPatch: { + int32_t* xDivs = NULL; + int32_t* yDivs = NULL; + uint32_t xDivsCount = 0; + uint32_t yDivsCount = 0; + + SkBitmap* bitmap = getBitmap(); + + xDivs = getInts(xDivsCount); + yDivs = getInts(yDivsCount); + + renderer.drawPatch(bitmap, xDivs, yDivs, xDivsCount, yDivsCount, + getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawColor: { + renderer.drawColor(getInt(), (SkXfermode::Mode) getInt()); + } + break; + case DrawRect: { + renderer.drawRect(getFloat(), getFloat(), getFloat(), getFloat(), getPaint()); + } + break; + case DrawPath: { + renderer.drawPath(getPath(), getPaint()); + } + break; + case DrawLines: { + int count = 0; + float* points = getFloats(count); + renderer.drawLines(points, count, getPaint()); + } + break; + case DrawText: { + getText(&text); + renderer.drawText(text.text(), text.length(), getInt(), + getFloat(), getFloat(), getPaint()); + } + break; + case ResetShader: { + renderer.resetShader(); + } + break; + case SetupShader: { + // TODO: Implement + } + break; + case ResetColorFilter: { + renderer.resetColorFilter(); + } + break; + case SetupColorFilter: { + // TODO: Implement + } + break; + case ResetShadow: { + renderer.resetShadow(); + } + break; + case SetupShadow: { + renderer.setupShadow(getFloat(), getFloat(), getFloat(), getInt()); + } + break; + } + } +} + +/////////////////////////////////////////////////////////////////////////////// // Base structure /////////////////////////////////////////////////////////////////////////////// @@ -56,75 +279,89 @@ void DisplayListRenderer::reset() { // Operations /////////////////////////////////////////////////////////////////////////////// +void DisplayListRenderer::setViewport(int width, int height) { + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + + mWidth = width; + mHeight = height; +} + +void DisplayListRenderer::prepare() { + mSnapshot = new Snapshot(mFirstSnapshot, + SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); + mSaveCount = 1; + mSnapshot->setClip(0.0f, 0.0f, mWidth, mHeight); +} + void DisplayListRenderer::acquireContext() { - addOp(AcquireContext); + addOp(DisplayList::AcquireContext); OpenGLRenderer::acquireContext(); } void DisplayListRenderer::releaseContext() { - addOp(ReleaseContext); + addOp(DisplayList::ReleaseContext); OpenGLRenderer::releaseContext(); } int DisplayListRenderer::save(int flags) { - addOp(Save); + addOp(DisplayList::Save); addInt(flags); return OpenGLRenderer::save(flags); } void DisplayListRenderer::restore() { - addOp(Restore); + addOp(DisplayList::Restore); OpenGLRenderer::restore(); } void DisplayListRenderer::restoreToCount(int saveCount) { - addOp(RestoreToCount); + addOp(DisplayList::RestoreToCount); addInt(saveCount); OpenGLRenderer::restoreToCount(saveCount); } int DisplayListRenderer::saveLayer(float left, float top, float right, float bottom, const SkPaint* p, int flags) { - addOp(SaveLayer); + addOp(DisplayList::SaveLayer); addBounds(left, top, right, bottom); addPaint(p); addInt(flags); - return OpenGLRenderer::saveLayer(left, top, right, bottom, p, flags); + return OpenGLRenderer::save(flags); } void DisplayListRenderer::translate(float dx, float dy) { - addOp(Translate); + addOp(DisplayList::Translate); addPoint(dx, dy); OpenGLRenderer::translate(dx, dy); } void DisplayListRenderer::rotate(float degrees) { - addOp(Rotate); + addOp(DisplayList::Rotate); addFloat(degrees); OpenGLRenderer::rotate(degrees); } void DisplayListRenderer::scale(float sx, float sy) { - addOp(Scale); + addOp(DisplayList::Scale); addPoint(sx, sy); OpenGLRenderer::scale(sx, sy); } void DisplayListRenderer::setMatrix(SkMatrix* matrix) { - addOp(SetMatrix); + addOp(DisplayList::SetMatrix); addMatrix(matrix); OpenGLRenderer::setMatrix(matrix); } void DisplayListRenderer::concatMatrix(SkMatrix* matrix) { - addOp(ConcatMatrix); + addOp(DisplayList::ConcatMatrix); addMatrix(matrix); OpenGLRenderer::concatMatrix(matrix); } bool DisplayListRenderer::clipRect(float left, float top, float right, float bottom, SkRegion::Op op) { - addOp(ClipRect); + addOp(DisplayList::ClipRect); addBounds(left, top, right, bottom); addInt(op); return OpenGLRenderer::clipRect(left, top, right, bottom, op); @@ -132,88 +369,77 @@ bool DisplayListRenderer::clipRect(float left, float top, float right, float bot void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) { - addOp(DrawBitmap); + addOp(DisplayList::DrawBitmap); addBitmap(bitmap); addPoint(left, top); addPaint(paint); - OpenGLRenderer::drawBitmap(bitmap, left, top, paint); } void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) { - addOp(DrawBitmapMatrix); + addOp(DisplayList::DrawBitmapMatrix); addBitmap(bitmap); addMatrix(matrix); addPaint(paint); - OpenGLRenderer::drawBitmap(bitmap, matrix, paint); } void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint) { - addOp(DrawBitmapRect); + addOp(DisplayList::DrawBitmapRect); addBitmap(bitmap); addBounds(srcLeft, srcTop, srcRight, srcBottom); addBounds(dstLeft, dstTop, dstRight, dstBottom); addPaint(paint); - OpenGLRenderer::drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom, - dstLeft, dstTop, dstRight, dstBottom, paint); } void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, uint32_t width, uint32_t height, float left, float top, float right, float bottom, const SkPaint* paint) { - addOp(DrawPatch); + addOp(DisplayList::DrawPatch); addBitmap(bitmap); addInts(xDivs, width); addInts(yDivs, height); addBounds(left, top, right, bottom); addPaint(paint); - OpenGLRenderer::drawPatch(bitmap, xDivs, yDivs, width, height, - left, top, right, bottom, paint); } void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { - addOp(DrawColor); + addOp(DisplayList::DrawColor); addInt(color); addInt(mode); - OpenGLRenderer::drawColor(color, mode); } void DisplayListRenderer::drawRect(float left, float top, float right, float bottom, const SkPaint* paint) { - addOp(DrawRect); + addOp(DisplayList::DrawRect); addBounds(left, top, right, bottom); addPaint(paint); - OpenGLRenderer::drawRect(left, top, right, bottom, paint); } void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { - addOp(DrawPath); + addOp(DisplayList::DrawPath); addPath(path); addPaint(paint); - OpenGLRenderer::drawPath(path, paint); } void DisplayListRenderer::drawLines(float* points, int count, const SkPaint* paint) { - addOp(DrawLines); + addOp(DisplayList::DrawLines); addFloats(points, count); addPaint(paint); - OpenGLRenderer::drawLines(points, count, paint); } void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint) { - addOp(DrawText); + addOp(DisplayList::DrawText); addText(text, bytesCount); addInt(count); addPoint(x, y); addPaint(paint); - OpenGLRenderer::drawText(text, bytesCount, count, x, y, paint); } void DisplayListRenderer::resetShader() { - addOp(ResetShader); + addOp(DisplayList::ResetShader); OpenGLRenderer::resetShader(); } @@ -223,7 +449,7 @@ void DisplayListRenderer::setupShader(SkiaShader* shader) { } void DisplayListRenderer::resetColorFilter() { - addOp(ResetColorFilter); + addOp(DisplayList::ResetColorFilter); OpenGLRenderer::resetColorFilter(); } @@ -233,12 +459,12 @@ void DisplayListRenderer::setupColorFilter(SkiaColorFilter* filter) { } void DisplayListRenderer::resetShadow() { - addOp(ResetShadow); + addOp(DisplayList::ResetShadow); OpenGLRenderer::resetShadow(); } void DisplayListRenderer::setupShadow(float radius, float dx, float dy, int color) { - addOp(SetupShadow); + addOp(DisplayList::SetupShadow); addFloat(radius); addPoint(dx, dy); addInt(color); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 7a20b59..735f0e7 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -81,7 +81,7 @@ public: int count() const { return mPaths.count(); } - const SkPath& operator[](int index) const { + SkPath& operator[](int index) const { return *mPaths[index]; } @@ -103,16 +103,18 @@ private: }; /////////////////////////////////////////////////////////////////////////////// -// Renderer +// Display list /////////////////////////////////////////////////////////////////////////////// +class DisplayListRenderer; + /** - * Records drawing commands in a display list for latter playback. + * Replays recorded drawing commands. */ -class DisplayListRenderer: public OpenGLRenderer { +class DisplayList { public: - DisplayListRenderer(); - ~DisplayListRenderer(); + DisplayList(const DisplayListRenderer& recorder); + ~DisplayList(); enum Op { AcquireContext, @@ -121,7 +123,6 @@ public: Restore, RestoreToCount, SaveLayer, - SaveLayerAlpha, Translate, Rotate, Scale, @@ -145,6 +146,109 @@ public: SetupShadow }; + void replay(OpenGLRenderer& renderer); + +private: + void init(); + + class TextContainer { + public: + size_t length() const { + return mByteLength; + } + + const char* text() const { + return (const char*) mText; + } + + size_t mByteLength; + const char* mText; + }; + + SkBitmap* getBitmap() { + int index = getInt(); + return &mBitmaps[index - 1]; + } + + inline int getIndex() { + return mReader.readInt(); + } + + inline int getInt() { + return mReader.readInt(); + } + + SkMatrix* getMatrix() { + int index = getInt(); + if (index == 0) { + return NULL; + } + return &mMatrices[index - 1]; + } + + SkPath* getPath() { + return &(*mPathHeap)[getInt() - 1]; + } + + SkPaint* getPaint() { + int index = getInt(); + if (index == 0) { + return NULL; + } + return &mPaints[index - 1]; + } + + inline float getFloat() { + return mReader.readScalar(); + } + + int32_t* getInts(uint32_t& count) { + count = getInt(); + return (int32_t*) mReader.skip(count * sizeof(int32_t)); + } + + float* getFloats(int& count) { + count = getInt(); + return (float*) mReader.skip(count * sizeof(float)); + } + + void getText(TextContainer* text) { + size_t length = text->mByteLength = getInt(); + text->mText = (const char*) mReader.skip(length); + } + + PathHeap* mPathHeap; + + SkBitmap* mBitmaps; + int mBitmapCount; + + SkMatrix* mMatrices; + int mMatrixCount; + + SkPaint* mPaints; + int mPaintCount; + + mutable SkFlattenableReadBuffer mReader; + + SkRefCntPlayback mRCPlayback; + SkTypefacePlayback mTFPlayback; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Renderer +/////////////////////////////////////////////////////////////////////////////// + +/** + * Records drawing commands in a display list for latter playback. + */ +class DisplayListRenderer: public OpenGLRenderer { +public: + DisplayListRenderer(); + ~DisplayListRenderer(); + + void setViewport(int width, int height); + void prepare(); + void acquireContext(); void releaseContext(); @@ -189,8 +293,28 @@ public: void reset(); + DisplayList* getDisplayList() const { + return new DisplayList(*this); + } + + const SkWriter32& writeStream() const { + return mWriter; + } + + const SkTDArray<const SkFlatBitmap*>& getBitmaps() const { + return mBitmaps; + } + + const SkTDArray<const SkFlatMatrix*>& getMatrices() const { + return mMatrices; + } + + const SkTDArray<const SkFlatPaint*>& getPaints() const { + return mPaints; + } + private: - inline void addOp(Op drawOp) { + inline void addOp(DisplayList::Op drawOp) { mWriter.writeInt(drawOp); } @@ -199,6 +323,7 @@ private: } void addInts(const int32_t* values, uint32_t count) { + mWriter.writeInt(count); for (uint32_t i = 0; i < count; i++) { mWriter.writeInt(values[i]); } @@ -209,6 +334,7 @@ private: } void addFloats(const float* values, int count) { + mWriter.writeInt(count); for (int i = 0; i < count; i++) { mWriter.writeScalar(values[i]); } @@ -273,6 +399,8 @@ private: SkRefCntRecorder mRCRecorder; SkRefCntRecorder mTFRecorder; + friend class DisplayList; + }; // class DisplayListRenderer }; // namespace uirenderer diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index d505d80..1974cf0 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -54,6 +54,8 @@ namespace uirenderer { // Renderer /////////////////////////////////////////////////////////////////////////////// +class DisplayListRenderer; + /** * OpenGL renderer used to draw accelerated 2D graphics. The API is a * simplified version of Skia's Canvas API. @@ -63,7 +65,7 @@ public: OpenGLRenderer(); virtual ~OpenGLRenderer(); - void setViewport(int width, int height); + virtual void setViewport(int width, int height); virtual void prepare(); virtual void finish(); @@ -428,6 +430,8 @@ private: // Misc GLint mMaxTextureSize; + friend class DisplayListRenderer; + }; // class OpenGLRenderer }; // namespace uirenderer |