diff options
-rw-r--r-- | api/current.txt | 15 | ||||
-rw-r--r-- | core/java/android/view/GLES20Canvas.java | 5 | ||||
-rw-r--r-- | core/java/android/view/GLES20Layer.java | 81 | ||||
-rw-r--r-- | core/java/android/view/GLES20RenderLayer.java | 91 | ||||
-rw-r--r-- | core/java/android/view/GLES20TextureLayer.java | 76 | ||||
-rw-r--r-- | core/java/android/view/HardwareLayer.java | 12 | ||||
-rw-r--r-- | core/java/android/view/HardwareRenderer.java | 52 | ||||
-rw-r--r-- | core/java/android/view/TextureView.java | 299 | ||||
-rw-r--r-- | core/jni/android_view_GLES20Canvas.cpp | 22 | ||||
-rw-r--r-- | libs/hwui/Layer.h | 18 | ||||
-rw-r--r-- | libs/hwui/LayerCache.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.cpp | 47 | ||||
-rw-r--r-- | libs/hwui/LayerRenderer.h | 3 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 57 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.h | 13 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.cpp | 34 | ||||
-rw-r--r-- | libs/hwui/ProgramCache.h | 5 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 14 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java | 87 |
19 files changed, 847 insertions, 86 deletions
diff --git a/api/current.txt b/api/current.txt index ebf3893..d568254 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20962,6 +20962,21 @@ package android.view { method public void setZOrderOnTop(boolean); } + public class TextureView extends android.view.View { + ctor public TextureView(android.content.Context); + ctor public TextureView(android.content.Context, android.util.AttributeSet); + ctor public TextureView(android.content.Context, android.util.AttributeSet, int); + method public final void draw(android.graphics.Canvas); + method public android.graphics.SurfaceTexture getSurfaceTexture(); + method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener(); + method protected final void onDraw(android.graphics.Canvas); + method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener); + } + + public static abstract interface TextureView.SurfaceTextureListener { + method public abstract void onSurfaceTextureAvailable(android.graphics.SurfaceTexture); + } + public class TouchDelegate { ctor public TouchDelegate(android.graphics.Rect, android.view.View); method public boolean onTouchEvent(android.view.MotionEvent); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index b8c5c2a..cdf8954 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -160,8 +160,11 @@ class GLES20Canvas extends HardwareCanvas { // Hardware layers /////////////////////////////////////////////////////////////////////////// + static native int nCreateTextureLayer(int[] layerInfo); static native int nCreateLayer(int width, int height, boolean isOpaque, int[] layerInfo); static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo); + static native void nUpdateTextureLayer(int layerId, int width, int height, + float[] textureTransform); static native void nDestroyLayer(int layerId); static native void nDestroyLayerDeferred(int layerId); @@ -272,7 +275,7 @@ class GLES20Canvas extends HardwareCanvas { } private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint); - + void interrupt() { nInterrupt(mRenderer); } diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index 6000a4a..bc191a6 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -14,39 +14,21 @@ * limitations under the License. */ -package android.view; -import android.graphics.Canvas; +package android.view; /** * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. */ -class GLES20Layer extends HardwareLayer { - private int mLayer; - - private int mLayerWidth; - private int mLayerHeight; - - private final GLES20Canvas mCanvas; +abstract class GLES20Layer extends HardwareLayer { + int mLayer; + Finalizer mFinalizer; - @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) - private final Finalizer mFinalizer; - - GLES20Layer(int width, int height, boolean isOpaque) { - super(width, height, isOpaque); - - int[] layerInfo = new int[2]; - mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo); - if (mLayer != 0) { - mLayerWidth = layerInfo[0]; - mLayerHeight = layerInfo[1]; + GLES20Layer() { + } - mCanvas = new GLES20Canvas(mLayer, !isOpaque); - mFinalizer = new Finalizer(mLayer); - } else { - mCanvas = null; - mFinalizer = null; - } + GLES20Layer(int width, int height, boolean opaque) { + super(width, height, opaque); } /** @@ -58,55 +40,14 @@ class GLES20Layer extends HardwareLayer { return mLayer; } - @Override - boolean isValid() { - return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0; - } - - @Override - void resize(int width, int height) { - if (!isValid() || width <= 0 || height <= 0) return; - - mWidth = width; - mHeight = height; - - if (width != mLayerWidth || height != mLayerHeight) { - int[] layerInfo = new int[2]; - - GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo); - - mLayerWidth = layerInfo[0]; - mLayerHeight = layerInfo[1]; - } - } - - @Override - HardwareCanvas getCanvas() { - return mCanvas; - } - - @Override - void end(Canvas currentCanvas) { - if (currentCanvas instanceof GLES20Canvas) { - ((GLES20Canvas) currentCanvas).resume(); - } - } - - @Override - HardwareCanvas start(Canvas currentCanvas) { - if (currentCanvas instanceof GLES20Canvas) { - ((GLES20Canvas) currentCanvas).interrupt(); - } - return getCanvas(); - } - + @Override void destroy() { - mFinalizer.destroy(); + if (mFinalizer != null) mFinalizer.destroy(); mLayer = 0; } - private static class Finalizer { + static class Finalizer { private int mLayerId; public Finalizer(int layerId) { diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java new file mode 100644 index 0000000..7adac1c --- /dev/null +++ b/core/java/android/view/GLES20RenderLayer.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2011 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.Canvas; + +/** + * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This + * implementation can be used a rendering target. It generates a + * {@link Canvas} that can be used to render into an FBO using OpenGL. + */ +class GLES20RenderLayer extends GLES20Layer { + + private int mLayerWidth; + private int mLayerHeight; + + private final GLES20Canvas mCanvas; + + GLES20RenderLayer(int width, int height, boolean isOpaque) { + super(width, height, isOpaque); + + int[] layerInfo = new int[2]; + mLayer = GLES20Canvas.nCreateLayer(width, height, isOpaque, layerInfo); + if (mLayer != 0) { + mLayerWidth = layerInfo[0]; + mLayerHeight = layerInfo[1]; + + mCanvas = new GLES20Canvas(mLayer, !isOpaque); + mFinalizer = new Finalizer(mLayer); + } else { + mCanvas = null; + mFinalizer = null; + } + } + + @Override + boolean isValid() { + return mLayer != 0 && mLayerWidth > 0 && mLayerHeight > 0; + } + + @Override + void resize(int width, int height) { + if (!isValid() || width <= 0 || height <= 0) return; + + mWidth = width; + mHeight = height; + + if (width != mLayerWidth || height != mLayerHeight) { + int[] layerInfo = new int[2]; + + GLES20Canvas.nResizeLayer(mLayer, width, height, layerInfo); + + mLayerWidth = layerInfo[0]; + mLayerHeight = layerInfo[1]; + } + } + + @Override + HardwareCanvas getCanvas() { + return mCanvas; + } + + @Override + void end(Canvas currentCanvas) { + if (currentCanvas instanceof GLES20Canvas) { + ((GLES20Canvas) currentCanvas).resume(); + } + } + + @Override + HardwareCanvas start(Canvas currentCanvas) { + if (currentCanvas instanceof GLES20Canvas) { + ((GLES20Canvas) currentCanvas).interrupt(); + } + return getCanvas(); + } +} diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java new file mode 100644 index 0000000..21fbdfc --- /dev/null +++ b/core/java/android/view/GLES20TextureLayer.java @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2011 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.Canvas; +import android.graphics.SurfaceTexture; + +/** + * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This + * implementation can be used as a texture. Rendering into this + * layer is not controlled by a {@link HardwareCanvas}. + */ +class GLES20TextureLayer extends GLES20Layer { + private int mTexture; + private SurfaceTexture mSurface; + + GLES20TextureLayer() { + int[] layerInfo = new int[2]; + mLayer = GLES20Canvas.nCreateTextureLayer(layerInfo); + + if (mLayer != 0) { + mTexture = layerInfo[0]; + mFinalizer = new Finalizer(mLayer); + } else { + mFinalizer = null; + } + } + + @Override + boolean isValid() { + return mLayer != 0 && mTexture != 0; + } + + @Override + void resize(int width, int height) { + } + + @Override + HardwareCanvas getCanvas() { + return null; + } + + @Override + HardwareCanvas start(Canvas currentCanvas) { + return null; + } + + @Override + void end(Canvas currentCanvas) { + } + + SurfaceTexture getSurfaceTexture() { + if (mSurface == null) { + mSurface = new SurfaceTexture(mTexture); + } + return mSurface; + } + + void update(int width, int height, float[] textureTransform) { + GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, textureTransform); + } +} diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index d01b8ce..86dec3f 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -26,12 +26,24 @@ import android.graphics.Canvas; * drawn several times. */ abstract class HardwareLayer { + /** + * Indicates an unknown dimension (width or height.) + */ + static final int DIMENSION_UNDEFINED = -1; + int mWidth; int mHeight; final boolean mOpaque; /** + * Creates a new hardware layer with undefined dimensions. + */ + HardwareLayer() { + this(DIMENSION_UNDEFINED, DIMENSION_UNDEFINED, false); + } + + /** * Creates a new hardware layer at least as large as the supplied * dimensions. * diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 66f37f2..5b2983d 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -20,6 +20,7 @@ package android.view; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.os.*; import android.util.EventLog; import android.util.Log; @@ -166,6 +167,14 @@ public abstract class HardwareRenderer { abstract DisplayList createDisplayList(View v); /** + * 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. + * + * @return A hardware layer + */ + abstract HardwareLayer createHardwareLayer(); + + /** * Creates a new hardware layer. * * @param width The minimum width of the layer @@ -175,6 +184,29 @@ public abstract class HardwareRenderer { * @return A hardware layer */ abstract HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque); + + /** + * Creates a new {@link SurfaceTexture} that can be used to render into the + * specified hardware layer. + * + * + * @param layer The layer to render into using a {@link android.graphics.SurfaceTexture} + * + * @return A {@link SurfaceTexture} + */ + abstract SurfaceTexture createSuraceTexture(HardwareLayer layer); + + /** + * Updates the specified layer. + * + * @param layer The hardware layer to update + * @param width The layer's width + * @param height The layer's height + * @param textureTransform A 4x4 column-first transform matrix to apply to + * texture coordinates + */ + abstract void updateTextureLayer(HardwareLayer layer, int width, int height, + float[] textureTransform); /** * Initializes the hardware renderer for the specified surface and setup the @@ -856,10 +888,26 @@ public abstract class HardwareRenderer { DisplayList createDisplayList(View v) { return new GLES20DisplayList(v); } - + + @Override + HardwareLayer createHardwareLayer() { + return new GLES20TextureLayer(); + } + @Override HardwareLayer createHardwareLayer(int width, int height, boolean isOpaque) { - return new GLES20Layer(width, height, isOpaque); + return new GLES20RenderLayer(width, height, isOpaque); + } + + @Override + SurfaceTexture createSuraceTexture(HardwareLayer layer) { + return ((GLES20TextureLayer) layer).getSurfaceTexture(); + } + + @Override + void updateTextureLayer(HardwareLayer layer, int width, int height, + float[] textureTransform) { + ((GLES20TextureLayer) layer).update(width, height, textureTransform); } static HardwareRenderer create(boolean translucent) { diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java new file mode 100644 index 0000000..5ece15c --- /dev/null +++ b/core/java/android/view/TextureView.java @@ -0,0 +1,299 @@ +/* + * Copyright (C) 2011 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.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.SurfaceTexture; +import android.util.AttributeSet; +import android.util.Log; + +/** + * <p>A TextureView can be used to display a content stream. Such a content + * stream can for instance be a video or an OpenGL scene. The content stream + * can come from the application's process as well as a remote process.</p> + * + * <p>TextureView can only be used in a hardware accelerated window. When + * rendered in software, TextureView will draw nothing.</p> + * + * <p>Unlike {@link SurfaceView}, TextureView does not create a separate + * window but behaves as a regular View. This key difference allows a + * TextureView to be moved, transformed, animated, etc. For instance, you + * can make a TextureView semi-translucent by calling + * <code>myView.setAlpha(0.5f)</code>.</p> + * + * <p>Using a TextureView is simple: all you need to do is get its + * {@link SurfaceTexture}. The {@link SurfaceTexture} can then be used to + * render content. The following example demonstrates how to render the + * camera preview into a TextureView:</p> + * + * <pre> + * public class LiveCameraActivity extends Activity implements TextureView.SurfaceTextureListener { + * private Camera mCamera; + * private TextureView mTextureView; + * + * @Override + * protected void onCreate(Bundle savedInstanceState) { + * super.onCreate(savedInstanceState); + * + * mTextureView = new TextureView(this); + * mTextureView.setSurfaceTextureListener(this); + * + * setContentView(mTextureView); + * } + * + * @Override + * protected void onDestroy() { + * super.onDestroy(); + * + * mCamera.stopPreview(); + * mCamera.release(); + * } + * + * @Override + * public void onSurfaceTextureAvailable(SurfaceTexture surface) { + * mCamera = Camera.open(); + * + * try { + * mCamera.setPreviewTexture(surface); + * mCamera.startPreview(); + * } catch (IOException ioe) { + * // Something bad happened + * } + * } + * } + * </pre> + * + * <p>A TextureView's SurfaceTexture can be obtained either by invoking + * {@link #getSurfaceTexture()} or by using a {@link SurfaceTextureListener}. + * It is important to know that a SurfaceTexture is available only after the + * TextureView is attached to a window (and {@link #onAttachedToWindow()} has + * been invoked.) It is therefore highly recommended you use a listener to + * be notified when the SurfaceTexture becomes available.</p> + * + * @see SurfaceView + * @see SurfaceTexture + */ +public class TextureView extends View { + private HardwareLayer mLayer; + private SurfaceTexture mSurface; + private SurfaceTextureListener mListener; + + private final float[] mTextureTransform = new float[16]; + + private final Runnable mUpdateLayerAction = new Runnable() { + @Override + public void run() { + updateLayer(); + } + }; + + /** + * Creates a new TextureView. + * + * @param context The context to associate this view with. + */ + public TextureView(Context context) { + super(context); + init(); + } + + /** + * Creates a new TextureView. + * + * @param context The context to associate this view with. + * @param attrs The attributes of the XML tag that is inflating the view. + */ + @SuppressWarnings({"UnusedDeclaration"}) + public TextureView(Context context, AttributeSet attrs) { + super(context, attrs); + init(); + } + + /** + * Creates a new TextureView. + * + * @param context The context to associate this view with. + * @param attrs The attributes of the XML tag that is inflating the view. + * @param defStyle The default style to apply to this view. If 0, no style + * will be applied (beyond what is included in the theme). This may + * either be an attribute resource, whose value will be retrieved + * from the current theme, or an explicit style resource. + */ + @SuppressWarnings({"UnusedDeclaration"}) + public TextureView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + init(); + } + + private void init() { + mLayerPaint = new Paint(); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (!isHardwareAccelerated()) { + Log.w("TextureView", "A TextureView or a subclass can only be " + + "used with hardware acceleration enabled."); + } + } + + /** + * The layer type of a TextureView is ignored since a TextureView is always + * considered to act as a hardware layer. The optional paint supplied to this + * method will however be taken into account when rendering the content of + * this TextureView. + * + * @param layerType The ype of layer to use with this view, must be one of + * {@link #LAYER_TYPE_NONE}, {@link #LAYER_TYPE_SOFTWARE} or + * {@link #LAYER_TYPE_HARDWARE} + * @param paint The paint used to compose the layer. This argument is optional + * and can be null. It is ignored when the layer type is + * {@link #LAYER_TYPE_NONE} + */ + @Override + public void setLayerType(int layerType, Paint paint) { + if (paint != mLayerPaint) { + mLayerPaint = paint; + invalidate(); + } + } + + /** + * Always returns {@link #LAYER_TYPE_HARDWARE}. + */ + @Override + public int getLayerType() { + return LAYER_TYPE_HARDWARE; + } + + /** + * Calling this method has no effect. + */ + @Override + public void buildLayer() { + } + + /** + * Subclasses of TextureView cannot do their own rendering + * with the {@link Canvas} object. + * + * @param canvas The Canvas to which the View is rendered. + */ + @Override + public final void draw(Canvas canvas) { + super.draw(canvas); + } + + /** + * Subclasses of TextureView cannot do their own rendering + * with the {@link Canvas} object. + * + * @param canvas The Canvas to which the View is rendered. + */ + @Override + protected final void onDraw(Canvas canvas) { + } + + @Override + HardwareLayer getHardwareLayer() { + if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { + return null; + } + + if (mLayer == null) { + mLayer = mAttachInfo.mHardwareRenderer.createHardwareLayer(); + mSurface = mAttachInfo.mHardwareRenderer.createSuraceTexture(mLayer); + + mSurface.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + // Per SurfaceTexture's documentation, the callback may be invoked + // from an arbitrary thread + post(mUpdateLayerAction); + } + }); + + if (mListener != null) { + mListener.onSurfaceTextureAvailable(mSurface); + } + } + + return mLayer; + } + + private void updateLayer() { + if (mAttachInfo == null || mAttachInfo.mHardwareRenderer == null) { + return; + } + + mSurface.updateTexImage(); + mSurface.getTransformMatrix(mTextureTransform); + + mAttachInfo.mHardwareRenderer.updateTextureLayer(mLayer, getWidth(), getHeight(), + mTextureTransform); + + invalidate(); + } + + /** + * Returns the {@link SurfaceTexture} used by this view. This method + * may return null if the view is not attached to a window. + */ + public SurfaceTexture getSurfaceTexture() { + return mSurface; + } + + /** + * Returns the {@link SurfaceTextureListener} currently associated with this + * texture view. + * + * @see #setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener) + * @see SurfaceTextureListener + */ + public SurfaceTextureListener getSurfaceTextureListener() { + return mListener; + } + + /** + * Sets the {@link SurfaceTextureListener} used to listen to surface + * texture events. + * + * @see #getSurfaceTextureListener() + * @see SurfaceTextureListener + */ + public void setSurfaceTextureListener(SurfaceTextureListener listener) { + mListener = listener; + } + + /** + * This listener can be used to be notified when the surface texture + * associated with this texture view is available. + */ + public static interface SurfaceTextureListener { + /** + * Invoked when a {@link TextureView}'s SurfaceTexture is ready for use. + * + * @param surface The surface returned by + * {@link android.view.TextureView#getSurfaceTexture()} + */ + public void onSurfaceTextureAvailable(SurfaceTexture surface); + } +} diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 314c2ee..af7639a 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -539,6 +539,19 @@ static OpenGLRenderer* android_view_GLES20Canvas_createLayerRenderer(JNIEnv* env return NULL; } +static Layer* android_view_GLES20Canvas_createTextureLayer(JNIEnv* env, jobject clazz, + jintArray layerInfo) { + Layer* layer = LayerRenderer::createTextureLayer(); + + if (layer) { + jint* storage = env->GetIntArrayElements(layerInfo, NULL); + storage[0] = layer->texture; + env->ReleaseIntArrayElements(layerInfo, storage, 0); + } + + return layer; +} + static Layer* android_view_GLES20Canvas_createLayer(JNIEnv* env, jobject clazz, jint width, jint height, jboolean isOpaque, jintArray layerInfo) { Layer* layer = LayerRenderer::createLayer(width, height, isOpaque); @@ -563,6 +576,13 @@ static void android_view_GLES20Canvas_resizeLayer(JNIEnv* env, jobject clazz, env->ReleaseIntArrayElements(layerInfo, storage, 0); } +static void android_view_GLES20Canvas_updateTextureLayer(JNIEnv* env, jobject clazz, + Layer* layer, jint width, jint height, jfloatArray texTransform) { + jfloat* transform = env->GetFloatArrayElements(texTransform, NULL); + LayerRenderer::updateTextureLayer(layer, width, height, transform); + env->ReleaseFloatArrayElements(texTransform, transform, 0); +} + static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) { LayerRenderer::destroyLayer(layer); } @@ -696,6 +716,8 @@ static JNINativeMethod gMethods[] = { { "nCreateLayerRenderer", "(I)I", (void*) android_view_GLES20Canvas_createLayerRenderer }, { "nCreateLayer", "(IIZ[I)I", (void*) android_view_GLES20Canvas_createLayer }, { "nResizeLayer", "(III[I)V" , (void*) android_view_GLES20Canvas_resizeLayer }, + { "nCreateTextureLayer", "([I)I", (void*) android_view_GLES20Canvas_createTextureLayer }, + { "nUpdateTextureLayer", "(III[F)V" , (void*) android_view_GLES20Canvas_updateTextureLayer }, { "nDestroyLayer", "(I)V", (void*) android_view_GLES20Canvas_destroyLayer }, { "nDestroyLayerDeferred", "(I)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred }, { "nDrawLayer", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawLayer }, diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 6c4a2a9..16566b8 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -45,6 +45,8 @@ struct Layer { mesh = NULL; meshIndices = NULL; meshElementCount = 0; + isCacheable = true; + isTextureLayer = false; } ~Layer() { @@ -137,6 +139,22 @@ struct Layer { TextureVertex* mesh; uint16_t* meshIndices; GLsizei meshElementCount; + + /** + * If set to true (by default), the layer can be reused. + */ + bool isCacheable; + + /** + * When set to true, this layer must be treated as a texture + * layer. + */ + bool isTextureLayer; + + /** + * Optional texture coordinates transform. + */ + mat4 texTransform; }; // struct Layer }; // namespace uirenderer diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index a9710ad..b2d795f 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -154,6 +154,8 @@ bool LayerCache::resize(Layer* layer, const uint32_t width, const uint32_t heigh } bool LayerCache::put(Layer* layer) { + if (!layer->isCacheable) return false; + const uint32_t size = layer->width * layer->height * 4; // Don't even try to cache a layer that's bigger than the cache if (size < mMaxSize) { diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index ca1e7ae..e167336 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -20,6 +20,7 @@ #include "LayerCache.h" #include "LayerRenderer.h" +#include "Matrix.h" #include "Properties.h" #include "Rect.h" @@ -165,6 +166,40 @@ void LayerRenderer::generateMesh() { // Layers management /////////////////////////////////////////////////////////////////////////////// +Layer* LayerRenderer::createTextureLayer() { + LAYER_RENDERER_LOGD("Creating new texture layer"); + + Layer* layer = new Layer(0, 0); + layer->isCacheable = false; + layer->isTextureLayer = true; + layer->blend = true; + layer->empty = true; + layer->fbo = 0; + layer->colorFilter = NULL; + layer->fbo = 0; + layer->layer.set(0.0f, 0.0f, 0.0f, 0.0f); + layer->texCoords.set(0.0f, 1.0f, 0.0f, 1.0f); + layer->alpha = 255; + layer->mode = SkXfermode::kSrcOver_Mode; + layer->colorFilter = NULL; + layer->region.clear(); + + glActiveTexture(GL_TEXTURE0); + + glGenTextures(1, &layer->texture); + glBindTexture(GL_TEXTURE_EXTERNAL_OES, layer->texture); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); + + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + return layer; +} + Layer* LayerRenderer::createLayer(uint32_t width, uint32_t height, bool isOpaque) { LAYER_RENDERER_LOGD("Creating new layer %dx%d", width, height); @@ -244,6 +279,18 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } +void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + float* transform) { + if (layer) { + layer->width = width; + layer->height = height; + layer->layer.set(0.0f, 0.0f, width, height); + layer->region.set(width, height); + layer->regionRect.set(0.0f, 0.0f, width, height); + layer->texTransform.load(transform); + } +} + void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { LAYER_RENDERER_LOGD("Destroying layer, fbo = %d", layer->fbo); diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index d2f565e..b3cd5db 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -53,8 +53,11 @@ public: Region* getRegion(); GLint getTargetFbo(); + static Layer* createTextureLayer(); static Layer* createLayer(uint32_t width, uint32_t height, bool isOpaque = false); static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + float* transform); static void destroyLayer(Layer* layer); static void destroyLayerDeferred(Layer* layer); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e926d99..4dde342 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -633,15 +633,43 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { } } +void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) { + float alpha = layer->alpha / 255.0f; + + setupDraw(); + setupDrawWithExternalTexture(); + setupDrawColor(alpha, alpha, alpha, alpha); + setupDrawColorFilter(); + setupDrawBlending(layer->blend, layer->mode); + setupDrawProgram(); + setupDrawModelView(rect.left, rect.top, rect.right, rect.bottom); + setupDrawPureColorUniforms(); + setupDrawColorFilterUniforms(); + setupDrawExternalTexture(layer->texture); + setupDrawTextureTransform(layer->texTransform); + setupDrawMesh(&mMeshVertices[0].position[0], &mMeshVertices[0].texture[0]); + + glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); + + finishDrawTexture(); +} + void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) { - const Rect& texCoords = layer->texCoords; - resetDrawTextureTexCoords(texCoords.left, texCoords.top, texCoords.right, texCoords.bottom); + if (!layer->isTextureLayer) { + const Rect& texCoords = layer->texCoords; + resetDrawTextureTexCoords(texCoords.left, texCoords.top, + texCoords.right, texCoords.bottom); - drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, - layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], - &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + layer->alpha / 255.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], GL_TRIANGLE_STRIP, gMeshCount, swap, swap); - resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + } else { + resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); + drawTextureLayer(layer, rect); + resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); + } } void OpenGLRenderer::composeLayerRegion(Layer* layer, const Rect& rect) { @@ -882,6 +910,10 @@ void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { mDescription.hasAlpha8Texture = isAlpha8; } +void OpenGLRenderer::setupDrawWithExternalTexture() { + mDescription.hasExternalTexture = true; +} + void OpenGLRenderer::setupDrawAALine() { mDescription.hasWidth = true; } @@ -1055,6 +1087,19 @@ void OpenGLRenderer::setupDrawTexture(GLuint texture) { glEnableVertexAttribArray(mTexCoordsSlot); } +void OpenGLRenderer::setupDrawExternalTexture(GLuint texture) { + bindExternalTexture(texture); + glUniform1i(mCaches.currentProgram->getUniform("sampler"), mTextureUnit++); + + mTexCoordsSlot = mCaches.currentProgram->getAttrib("texCoords"); + glEnableVertexAttribArray(mTexCoordsSlot); +} + +void OpenGLRenderer::setupDrawTextureTransform(mat4& transform) { + glUniformMatrix4fv(mCaches.currentProgram->getUniform("mainTextureTransform"), 1, + GL_FALSE, &transform.data[0]); +} + void OpenGLRenderer::setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLuint vbo) { if (!vertices) { mCaches.bindMeshBuffer(vbo == 0 ? mCaches.meshBuffer : vbo); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 0276095..a3e12f6 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -248,6 +248,8 @@ private: */ void composeLayerRect(Layer* layer, const Rect& rect, bool swap = false); + void drawTextureLayer(Layer* layer, const Rect& rect); + /** * Mark the layer as dirty at the specified coordinates. The coordinates * are transformed with the supplied matrix. @@ -400,6 +402,14 @@ private: } /** + * Binds the specified EGLImage texture. The texture unit must have been selected + * prior to calling this method. + */ + inline void bindExternalTexture(GLuint texture) { + glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture); + } + + /** * Sets the wrap modes for the specified texture. The wrap modes are modified * only when needed. */ @@ -438,6 +448,7 @@ private: * Various methods to setup OpenGL rendering. */ void setupDrawWithTexture(bool isAlpha8 = false); + void setupDrawWithExternalTexture(); void setupDrawAALine(); void setupDrawPoint(float pointSize); void setupDrawColor(int color); @@ -466,6 +477,8 @@ private: void setupDrawColorFilterUniforms(); void setupDrawSimpleMesh(); void setupDrawTexture(GLuint texture); + void setupDrawExternalTexture(GLuint texture); + void setupDrawTextureTransform(mat4& transform); void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0); void setupDrawVertices(GLvoid* vertices); void setupDrawAALine(GLvoid* vertices, GLvoid* distanceCoords, float strokeWidth); diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 80b1917..62ac2ba 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -41,6 +41,8 @@ const char* gVS_Header_Attributes_TexCoords = "attribute vec2 texCoords;\n"; const char* gVS_Header_Attributes_Distance = "attribute float vtxDistance;\n"; +const char* gVS_Header_Uniforms_TextureTransform = + "uniform mat4 mainTextureTransform;\n"; const char* gVS_Header_Uniforms = "uniform mat4 transform;\n"; const char* gVS_Header_Uniforms_IsPoint = @@ -76,6 +78,8 @@ const char* gVS_Main = "\nvoid main(void) {\n"; const char* gVS_Main_OutTexCoords = " outTexCoords = texCoords;\n"; +const char* gVS_Main_OutTransformedTexCoords = + " outTexCoords = (mainTextureTransform * vec4(texCoords, 0.0, 1.0)).xy;\n"; const char* gVS_Main_OutGradient[3] = { // Linear " linear = vec2((screenSpace * position).x, 0.5);\n", @@ -103,6 +107,8 @@ const char* gVS_Footer = const char* gFS_Header_Extension_FramebufferFetch = "#extension GL_NV_shader_framebuffer_fetch : enable\n\n"; +const char* gFS_Header_Extension_ExternalTexture = + "#extension GL_OES_EGL_image_external : require\n\n"; const char* gFS_Header = "precision mediump float;\n\n"; const char* gFS_Uniforms_Color = @@ -116,6 +122,8 @@ const char* gFS_Header_Uniforms_PointHasBitmap = "uniform float pointSize;\n"; const char* gFS_Uniforms_TextureSampler = "uniform sampler2D sampler;\n"; +const char* gFS_Uniforms_ExternalTextureSampler = + "uniform samplerExternalOES sampler;\n"; const char* gFS_Uniforms_GradientSampler[3] = { // Linear "uniform sampler2D gradientSampler;\n", @@ -369,7 +377,7 @@ Program* ProgramCache::generateProgram(const ProgramDescription& description, pr String8 ProgramCache::generateVertexShader(const ProgramDescription& description) { // Add attributes String8 shader(gVS_Header_Attributes); - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Attributes_TexCoords); } if (description.hasWidth) { @@ -377,6 +385,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description } // Uniforms shader.append(gVS_Header_Uniforms); + if (description.hasExternalTexture) { + shader.append(gVS_Header_Uniforms_TextureTransform); + } if (description.hasGradient) { shader.append(gVS_Header_Uniforms_HasGradient[description.gradientType]); } @@ -387,7 +398,7 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description shader.append(gVS_Header_Uniforms_IsPoint); } // Varyings - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); } if (description.hasWidth) { @@ -407,6 +418,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasTexture) { shader.append(gVS_Main_OutTexCoords); } + if (description.hasExternalTexture) { + shader.append(gVS_Main_OutTransformedTexCoords); + } if (description.hasWidth) { shader.append(gVS_Main_Width); } @@ -440,11 +454,14 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (blendFramebuffer) { shader.append(gFS_Header_Extension_FramebufferFetch); } + if (description.hasExternalTexture) { + shader.append(gFS_Header_Extension_ExternalTexture); + } shader.append(gFS_Header); // Varyings - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); } if (description.hasWidth) { @@ -461,7 +478,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Uniforms int modulateOp = MODULATE_OP_NO_MODULATE; - const bool singleColor = !description.hasTexture && + const bool singleColor = !description.hasTexture && !description.hasExternalTexture && !description.hasGradient && !description.hasBitmap; if (description.modulate || singleColor) { @@ -471,6 +488,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasTexture) { shader.append(gFS_Uniforms_TextureSampler); } + if (description.hasExternalTexture) { + shader.append(gFS_Uniforms_ExternalTextureSampler); + } if (description.hasWidth) { shader.append(gFS_Uniforms_Width); } @@ -487,11 +507,11 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; - const bool singleTexture = description.hasTexture && + const bool singleTexture = (description.hasTexture || description.hasExternalTexture) && !description.hasAlpha8Texture && noShader; const bool singleA8Texture = description.hasTexture && description.hasAlpha8Texture && noShader; - const bool singleGradient = !description.hasTexture && + const bool singleGradient = !description.hasTexture && !description.hasExternalTexture && description.hasGradient && !description.hasBitmap && description.gradientType == ProgramDescription::kGradientLinear; @@ -554,7 +574,7 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Begin the shader shader.append(gFS_Main); { // Stores the result in fragColor directly - if (description.hasTexture) { + if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { if (!description.hasGradient && !description.hasBitmap) { shader.append(gFS_Main_FetchA8Texture[modulateOp]); diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 18d98cb..70909fd 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -77,6 +77,8 @@ namespace uirenderer { #define PROGRAM_HAS_WIDTH_SHIFT 37 +#define PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT 38 + /////////////////////////////////////////////////////////////////////////////// // Types /////////////////////////////////////////////////////////////////////////////// @@ -113,6 +115,7 @@ struct ProgramDescription { // Texturing bool hasTexture; bool hasAlpha8Texture; + bool hasExternalTexture; // Modulate, this should only be set when setColor() return true bool modulate; @@ -151,6 +154,7 @@ struct ProgramDescription { void reset() { hasTexture = false; hasAlpha8Texture = false; + hasExternalTexture = false; hasWidth = false; @@ -240,6 +244,7 @@ struct ProgramDescription { if (modulate) key |= programid(0x1) << PROGRAM_MODULATE_SHIFT; if (isPoint) key |= programid(0x1) << PROGRAM_IS_POINT_SHIFT; if (hasWidth) key |= programid(0x1) << PROGRAM_HAS_WIDTH_SHIFT; + if (hasExternalTexture) key |= programid(0x1) << PROGRAM_HAS_EXTERNAL_TEXTURE_SHIFT; return key; } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index c763b1d..3a3bfd8 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -18,6 +18,11 @@ package="com.android.test.hwui"> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.CAMERA" /> + + <uses-feature android:name="android.hardware.camera" /> + <uses-feature android:name="android.hardware.camera.autofocus" /> + <uses-sdk android:minSdkVersion="11" /> <application @@ -25,6 +30,15 @@ android:hardwareAccelerated="true"> <activity + android:name="TextureViewActivity" + android:label="_TextureView"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="BitmapMeshActivity" android:label="_BitmapMesh"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java new file mode 100644 index 0000000..4726672 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 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.android.test.hwui; + +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.graphics.SurfaceTexture; +import android.hardware.Camera; +import android.os.Bundle; +import android.view.Gravity; +import android.view.TextureView; +import android.view.View; +import android.widget.FrameLayout; + +import java.io.IOException; + +@SuppressWarnings({"UnusedDeclaration"}) +public class TextureViewActivity extends Activity implements TextureView.SurfaceTextureListener { + private Camera mCamera; + private TextureView mTextureView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mTextureView = new TextureView(this); + mTextureView.setSurfaceTextureListener(this); + + setContentView(mTextureView, new FrameLayout.LayoutParams(500, 400, Gravity.CENTER)); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + mCamera.stopPreview(); + mCamera.release(); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface) { + mCamera = Camera.open(); + + try { + mCamera.setPreviewTexture(surface); + } catch (IOException t) { + android.util.Log.e("TextureView", "Cannot set preview texture target!", t); + } + + mCamera.startPreview(); + + mTextureView.setCameraDistance(5000); + + ObjectAnimator animator = ObjectAnimator.ofFloat(mTextureView, "rotationY", 0.0f, 360.0f); + animator.setRepeatMode(ObjectAnimator.REVERSE); + animator.setRepeatCount(ObjectAnimator.INFINITE); + animator.setDuration(4000); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + ((View) mTextureView.getParent()).invalidate(); + } + }); + animator.start(); + + animator = ObjectAnimator.ofFloat(mTextureView, "alpha", 1.0f, 0.0f); + animator.setRepeatMode(ObjectAnimator.REVERSE); + animator.setRepeatCount(ObjectAnimator.INFINITE); + animator.setDuration(4000); + animator.start(); + } +} |