summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/view/DisplayList.java55
-rw-r--r--core/java/android/view/GLES20Canvas.java71
-rw-r--r--core/java/android/view/GLES20DisplayList.java74
-rw-r--r--core/java/android/view/HardwareCanvas.java60
-rw-r--r--core/java/android/view/HardwareRenderer.java15
-rw-r--r--core/java/android/view/View.java101
-rw-r--r--core/java/android/view/ViewGroup.java33
-rw-r--r--core/java/android/view/ViewRoot.java35
-rw-r--r--core/jni/android_view_GLES20Canvas.cpp31
-rw-r--r--libs/hwui/DisplayListRenderer.cpp300
-rw-r--r--libs/hwui/DisplayListRenderer.h144
-rw-r--r--libs/hwui/OpenGLRenderer.h6
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