diff options
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | core/java/android/view/Surface.java | 123 | ||||
-rw-r--r-- | core/jni/android_view_Surface.cpp | 56 | ||||
-rw-r--r-- | libs/hwui/renderthread/TimeLord.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/tests/main.cpp | 4 | ||||
-rw-r--r-- | tests/HwAccelerationTest/AndroidManifest.xml | 18 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java | 124 | ||||
-rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java | 124 |
8 files changed, 436 insertions, 16 deletions
diff --git a/api/current.txt b/api/current.txt index caf28cb..9fe71d4 100644 --- a/api/current.txt +++ b/api/current.txt @@ -33369,6 +33369,7 @@ package android.view { method public int describeContents(); method public boolean isValid(); method public android.graphics.Canvas lockCanvas(android.graphics.Rect) throws java.lang.IllegalArgumentException, android.view.Surface.OutOfResourcesException; + method public android.graphics.Canvas lockHardwareCanvas(); method public void readFromParcel(android.os.Parcel); method public void release(); method public deprecated void unlockCanvas(android.graphics.Canvas); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 3770b8a..132e25c 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -87,6 +87,8 @@ public class Surface implements Parcelable { // non compatibility mode. private Matrix mCompatibleMatrix; + private HwuiContext mHwuiContext; + /** @hide */ @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) @Retention(RetentionPolicy.SOURCE) @@ -171,6 +173,10 @@ public class Surface implements Parcelable { nativeRelease(mNativeObject); setNativeObjectLocked(0); } + if (mHwuiContext != null) { + mHwuiContext.destroy(); + mHwuiContext = null; + } } } @@ -264,27 +270,60 @@ public class Surface implements Parcelable { * @param canvas The canvas previously obtained from {@link #lockCanvas}. */ public void unlockCanvasAndPost(Canvas canvas) { + synchronized (mLock) { + checkNotReleasedLocked(); + + if (mHwuiContext != null) { + mHwuiContext.unlockAndPost(canvas); + } else { + unlockSwCanvasAndPost(canvas); + } + } + } + + private void unlockSwCanvasAndPost(Canvas canvas) { if (canvas != mCanvas) { throw new IllegalArgumentException("canvas object must be the same instance that " + "was previously returned by lockCanvas"); } + if (mNativeObject != mLockedObject) { + Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + + Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + + Long.toHexString(mLockedObject) +")"); + } + if (mLockedObject == 0) { + throw new IllegalStateException("Surface was not locked"); + } + try { + nativeUnlockCanvasAndPost(mLockedObject, canvas); + } finally { + nativeRelease(mLockedObject); + mLockedObject = 0; + } + } + /** + * Gets a {@link Canvas} for drawing into this surface. + * + * After drawing into the provided {@link Canvas}, the caller must + * invoke {@link #unlockCanvasAndPost} to post the new contents to the surface. + * + * Unlike {@link #lockCanvas(Rect)} this will return a hardware-accelerated + * canvas. See the <a href="{@docRoot}guide/topics/graphics/hardware-accel.html#unsupported"> + * unsupported drawing operations</a> for a list of what is and isn't + * supported in a hardware-accelerated canvas. + * + * @return A canvas for drawing into the surface. + * + * @throws IllegalStateException If the canvas cannot be locked. + */ + public Canvas lockHardwareCanvas() { synchronized (mLock) { checkNotReleasedLocked(); - if (mNativeObject != mLockedObject) { - Log.w(TAG, "WARNING: Surface's mNativeObject (0x" + - Long.toHexString(mNativeObject) + ") != mLockedObject (0x" + - Long.toHexString(mLockedObject) +")"); - } - if (mLockedObject == 0) { - throw new IllegalStateException("Surface was not locked"); - } - try { - nativeUnlockCanvasAndPost(mLockedObject, canvas); - } finally { - nativeRelease(mLockedObject); - mLockedObject = 0; + if (mHwuiContext == null) { + mHwuiContext = new HwuiContext(); } + return mHwuiContext.lockCanvas(); } } @@ -415,6 +454,9 @@ public class Surface implements Parcelable { } mNativeObject = ptr; mGenerationId += 1; + if (mHwuiContext != null) { + mHwuiContext.updateSurface(); + } } } @@ -518,4 +560,59 @@ public class Surface implements Parcelable { mOrigMatrix.set(m); } } + + private final class HwuiContext { + private final RenderNode mRenderNode; + private long mHwuiRenderer; + private HardwareCanvas mCanvas; + + HwuiContext() { + mRenderNode = RenderNode.create("HwuiCanvas", null); + mRenderNode.setClipToBounds(false); + mHwuiRenderer = nHwuiCreate(mRenderNode.mNativeRenderNode, mNativeObject); + } + + Canvas lockCanvas() { + if (mCanvas != null) { + throw new IllegalStateException("Surface was already locked!"); + } + mCanvas = mRenderNode.start(0, 0); + return mCanvas; + } + + void unlockAndPost(Canvas canvas) { + if (canvas != mCanvas) { + throw new IllegalArgumentException("canvas object must be the same instance that " + + "was previously returned by lockCanvas"); + } + mRenderNode.end(mCanvas); + mCanvas = null; + nHwuiDraw(mHwuiRenderer); + } + + void updateSurface() { + nHwuiSetSurface(mHwuiRenderer, mNativeObject); + } + + void destroy() { + if (mHwuiRenderer != 0) { + nHwuiDestroy(mHwuiRenderer); + mHwuiRenderer = 0; + } + } + + @Override + protected void finalize() throws Throwable { + try { + destroy(); + } finally { + super.finalize(); + } + } + } + + private static native long nHwuiCreate(long rootNode, long surface); + private static native void nHwuiSetSurface(long renderer, long surface); + private static native void nHwuiDraw(long renderer); + private static native void nHwuiDestroy(long renderer); } diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 9bb8195..b3d9890 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -47,6 +47,11 @@ #include <ScopedUtfChars.h> +#include <AnimationContext.h> +#include <DisplayListRenderer.h> +#include <RenderNode.h> +#include <renderthread/RenderProxy.h> + // ---------------------------------------------------------------------------- namespace android { @@ -352,8 +357,53 @@ static void nativeWriteToParcel(JNIEnv* env, jclass clazz, parcel->writeStrongBinder( self != 0 ? self->getIGraphicBufferProducer()->asBinder() : NULL); } +namespace uirenderer { + +using namespace android::uirenderer::renderthread; + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContext(clock); + } +}; + +static jlong create(JNIEnv* env, jclass clazz, jlong rootNodePtr, jlong surfacePtr) { + RenderNode* rootNode = reinterpret_cast<RenderNode*>(rootNodePtr); + sp<Surface> surface(reinterpret_cast<Surface*>(surfacePtr)); + ContextFactory factory; + RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); + proxy->loadSystemProperties(); + proxy->initialize(surface); + // Shadows can't be used via this interface, so just set the light source + // to all 0s. (and width & height are unused, TODO remove them) + proxy->setup(0, 0, (Vector3){0, 0, 0}, 0, 0, 0); + return (jlong) proxy; +} + +static void setSurface(JNIEnv* env, jclass clazz, jlong rendererPtr, jlong surfacePtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(rendererPtr); + sp<Surface> surface(reinterpret_cast<Surface*>(surfacePtr)); + proxy->updateSurface(surface); +} + +static void draw(JNIEnv* env, jclass clazz, jlong rendererPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(rendererPtr); + nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); + proxy->syncAndDrawFrame(frameTimeNs, 0, 1.0f); +} + +static void destroy(JNIEnv* env, jclass clazz, jlong rendererPtr) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(rendererPtr); + delete proxy; +} + +} // uirenderer + // ---------------------------------------------------------------------------- +namespace hwui = android::uirenderer; + static JNINativeMethod gSurfaceMethods[] = { {"nativeCreateFromSurfaceTexture", "(Landroid/graphics/SurfaceTexture;)J", (void*)nativeCreateFromSurfaceTexture }, @@ -375,6 +425,12 @@ static JNINativeMethod gSurfaceMethods[] = { (void*)nativeReadFromParcel }, {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel }, + + // HWUI context + {"nHwuiCreate", "(JJ)J", (void*) hwui::create }, + {"nHwuiSetSurface", "(JJ)V", (void*) hwui::setSurface }, + {"nHwuiDraw", "(J)V", (void*) hwui::draw }, + {"nHwuiDestroy", "(J)V", (void*) hwui::destroy }, }; int register_android_view_Surface(JNIEnv* env) diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index cf3d039..9bd4eae 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -20,7 +20,7 @@ namespace uirenderer { namespace renderthread { TimeLord::TimeLord() - : mFrameIntervalNanos(0) + : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)) , mFrameTimeNanos(0) { } diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp index 3e9a86f..2b80d56 100644 --- a/libs/hwui/tests/main.cpp +++ b/libs/hwui/tests/main.cpp @@ -81,8 +81,8 @@ int main(int argc, char* argv[]) { rootNode->mutateStagingProperties().setClipToBounds(false); rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); - RenderProxy* proxy = new RenderProxy(false, rootNode, new ContextFactory()); - proxy->setFrameInterval(milliseconds_to_nanoseconds(16)); + ContextFactory factory; + RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); proxy->loadSystemProperties(); proxy->initialize(surface); float lightX = width / 2.0; diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index bc2f1fd..8531944 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -342,6 +342,24 @@ </activity> <activity + android:name="HardwareCanvasTextureViewActivity" + android:label="TextureView/HardwareCanvas"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity + android:name="HardwareCanvasSurfaceViewActivity" + android:label="SurfaceView/HardwareCanvas"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="com.android.test.hwui.TEST" /> + </intent-filter> + </activity> + + <activity android:name="GLTextureViewActivity" android:label="TextureView/OpenGL"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java new file mode 100644 index 0000000..b1431c5 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasSurfaceViewActivity.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2014 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.app.Activity; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.os.Bundle; +import android.view.Gravity; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceHolder.Callback; +import android.view.SurfaceView; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class HardwareCanvasSurfaceViewActivity extends Activity implements Callback { + private SurfaceView mSurfaceView; + private HardwareCanvasSurfaceViewActivity.RenderingThread mThread; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this); + + mSurfaceView = new SurfaceView(this); + mSurfaceView.getHolder().addCallback(this); + + content.addView(mSurfaceView, new FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + Gravity.CENTER)); + setContentView(content); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + mThread = new RenderingThread(holder.getSurface()); + mThread.start(); + } + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + mThread.setSize(width, height); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + if (mThread != null) mThread.stopRendering(); + } + + private static class RenderingThread extends Thread { + private final Surface mSurface; + private volatile boolean mRunning = true; + private int mWidth, mHeight; + + public RenderingThread(Surface surface) { + mSurface = surface; + } + + void setSize(int width, int height) { + mWidth = width; + mHeight = height; + } + + @Override + public void run() { + float x = 0.0f; + float y = 0.0f; + float speedX = 5.0f; + float speedY = 3.0f; + + Paint paint = new Paint(); + paint.setColor(0xff00ff00); + + while (mRunning && !Thread.interrupted()) { + final Canvas canvas = mSurface.lockHardwareCanvas(); + try { + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint); + } finally { + mSurface.unlockCanvasAndPost(canvas); + } + + if (x + 20.0f + speedX >= mWidth || x + speedX <= 0.0f) { + speedX = -speedX; + } + if (y + 20.0f + speedY >= mHeight || y + speedY <= 0.0f) { + speedY = -speedY; + } + + x += speedX; + y += speedY; + + try { + Thread.sleep(15); + } catch (InterruptedException e) { + // Interrupted + } + } + } + + void stopRendering() { + interrupt(); + mRunning = false; + } + } +} diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java new file mode 100644 index 0000000..63a6efa --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareCanvasTextureViewActivity.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2014 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.app.Activity; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.SurfaceTexture; +import android.os.Bundle; +import android.view.Gravity; +import android.view.Surface; +import android.view.TextureView; +import android.widget.FrameLayout; + +@SuppressWarnings({"UnusedDeclaration"}) +public class HardwareCanvasTextureViewActivity extends Activity + implements TextureView.SurfaceTextureListener { + private TextureView mTextureView; + private HardwareCanvasTextureViewActivity.RenderingThread mThread; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + FrameLayout content = new FrameLayout(this); + + mTextureView = new TextureView(this); + mTextureView.setSurfaceTextureListener(this); + mTextureView.setOpaque(false); + + content.addView(mTextureView, new FrameLayout.LayoutParams(500, 500, Gravity.CENTER)); + setContentView(content); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + mThread = new RenderingThread(mTextureView); + mThread.start(); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Ignored + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + if (mThread != null) mThread.stopRendering(); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) { + // Ignored + } + + private static class RenderingThread extends Thread { + private final TextureView mView; + private final Surface mSurface; + private volatile boolean mRunning = true; + + public RenderingThread(TextureView view) { + mView = view; + mSurface = new Surface(mView.getSurfaceTexture()); + } + + @Override + public void run() { + float x = 0.0f; + float y = 0.0f; + float speedX = 5.0f; + float speedY = 3.0f; + + Paint paint = new Paint(); + paint.setColor(0xff00ff00); + + while (mRunning && !Thread.interrupted()) { + final Canvas canvas = mSurface.lockHardwareCanvas(); + try { + canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); + canvas.drawRect(x, y, x + 20.0f, y + 20.0f, paint); + } finally { + mSurface.unlockCanvasAndPost(canvas); + } + + if (x + 20.0f + speedX >= mView.getWidth() || x + speedX <= 0.0f) { + speedX = -speedX; + } + if (y + 20.0f + speedY >= mView.getHeight() || y + speedY <= 0.0f) { + speedY = -speedY; + } + + x += speedX; + y += speedY; + + try { + Thread.sleep(15); + } catch (InterruptedException e) { + // Interrupted + } + } + } + + void stopRendering() { + interrupt(); + mRunning = false; + } + } +} |