am 29277c8c: Merge "RenderThread animator support"
* commit '29277c8c85092e3da9362cb62e96dae0ba4fec5e': RenderThread animator support
This commit is contained in:
@@ -20,6 +20,9 @@ import android.annotation.NonNull;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Outline;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* <p>A display list records a series of graphics related operations and can replay
|
||||
* them later. Display lists are usually built by recording operations on a
|
||||
@@ -176,10 +179,23 @@ public class RenderNode {
|
||||
private boolean mValid;
|
||||
private final long mNativeRenderNode;
|
||||
|
||||
// We need to keep a strong reference to all running animators to ensure that
|
||||
// they can call removeAnimator when they have finished, as the native-side
|
||||
// object can only hold a WeakReference<> to avoid leaking memory due to
|
||||
// cyclic references.
|
||||
private List<RenderNodeAnimator> mActiveAnimators;
|
||||
|
||||
private RenderNode(String name) {
|
||||
mNativeRenderNode = nCreate(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see RenderNode#adopt(long)
|
||||
*/
|
||||
private RenderNode(long nativePtr) {
|
||||
mNativeRenderNode = nativePtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new display list that can be used to record batches of
|
||||
* drawing operations.
|
||||
@@ -194,6 +210,17 @@ public class RenderNode {
|
||||
return new RenderNode(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adopts an existing native render node.
|
||||
*
|
||||
* Note: This will *NOT* incRef() on the native object, however it will
|
||||
* decRef() when it is destroyed. The caller should have already incRef'd it
|
||||
*/
|
||||
public static RenderNode adopt(long nativePtr) {
|
||||
return new RenderNode(nativePtr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Starts recording a display list for the render node. All
|
||||
* operations performed on the returned canvas are recorded and
|
||||
@@ -821,6 +848,23 @@ public class RenderNode {
|
||||
nOutput(mNativeRenderNode);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Animations
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addAnimator(RenderNodeAnimator animator) {
|
||||
if (mActiveAnimators == null) {
|
||||
mActiveAnimators = new ArrayList<RenderNodeAnimator>();
|
||||
}
|
||||
mActiveAnimators.add(animator);
|
||||
nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
|
||||
}
|
||||
|
||||
public void removeAnimator(RenderNodeAnimator animator) {
|
||||
nRemoveAnimator(mNativeRenderNode, animator.getNativeAnimator());
|
||||
mActiveAnimators.remove(animator);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Native methods
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@@ -895,6 +939,13 @@ public class RenderNode {
|
||||
private static native float nGetPivotY(long renderNode);
|
||||
private static native void nOutput(long renderNode);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Animations
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static native void nAddAnimator(long renderNode, long animatorPtr);
|
||||
private static native void nRemoveAnimator(long renderNode, long animatorPtr);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Finalization
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
122
core/java/android/view/RenderNodeAnimator.java
Normal file
122
core/java/android/view/RenderNodeAnimator.java
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* 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 android.view;
|
||||
|
||||
import android.util.SparseIntArray;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public final class RenderNodeAnimator {
|
||||
|
||||
// Keep in sync with enum RenderProperty in Animator.h
|
||||
private static final int TRANSLATION_X = 0;
|
||||
private static final int TRANSLATION_Y = 1;
|
||||
private static final int TRANSLATION_Z = 2;
|
||||
private static final int SCALE_X = 3;
|
||||
private static final int SCALE_Y = 4;
|
||||
private static final int ROTATION = 5;
|
||||
private static final int ROTATION_X = 6;
|
||||
private static final int ROTATION_Y = 7;
|
||||
private static final int X = 8;
|
||||
private static final int Y = 9;
|
||||
private static final int Z = 10;
|
||||
private static final int ALPHA = 11;
|
||||
|
||||
// ViewPropertyAnimator uses a mask for its values, we need to remap them
|
||||
// to the enum values here. RenderPropertyAnimator can't use the mask values
|
||||
// directly as internally it uses a lookup table so it needs the values to
|
||||
// be sequential starting from 0
|
||||
private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
|
||||
put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
|
||||
put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
|
||||
put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
|
||||
put(ViewPropertyAnimator.SCALE_X, SCALE_X);
|
||||
put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
|
||||
put(ViewPropertyAnimator.ROTATION, ROTATION);
|
||||
put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
|
||||
put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
|
||||
put(ViewPropertyAnimator.X, X);
|
||||
put(ViewPropertyAnimator.Y, Y);
|
||||
put(ViewPropertyAnimator.Z, Z);
|
||||
put(ViewPropertyAnimator.ALPHA, ALPHA);
|
||||
}};
|
||||
|
||||
// Keep in sync DeltaValueType in Animator.h
|
||||
private static final int DELTA_TYPE_ABSOLUTE = 0;
|
||||
private static final int DELTA_TYPE_DELTA = 1;
|
||||
|
||||
private RenderNode mTarget;
|
||||
private long mNativePtr;
|
||||
|
||||
public int mapViewPropertyToRenderProperty(int viewProperty) {
|
||||
return sViewPropertyAnimatorMap.get(viewProperty);
|
||||
}
|
||||
|
||||
public RenderNodeAnimator(int property, int deltaType, float deltaValue) {
|
||||
mNativePtr = nCreateAnimator(new WeakReference<RenderNodeAnimator>(this),
|
||||
property, deltaType, deltaValue);
|
||||
}
|
||||
|
||||
public void start(View target) {
|
||||
mTarget = target.mRenderNode;
|
||||
mTarget.addAnimator(this);
|
||||
// Kick off a frame to start the process
|
||||
target.invalidateViewProperty(true, false);
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
mTarget.removeAnimator(this);
|
||||
}
|
||||
|
||||
public void setDuration(int duration) {
|
||||
nSetDuration(mNativePtr, duration);
|
||||
}
|
||||
|
||||
long getNativeAnimator() {
|
||||
return mNativePtr;
|
||||
}
|
||||
|
||||
private void onFinished() {
|
||||
mTarget.removeAnimator(this);
|
||||
}
|
||||
|
||||
// Called by native
|
||||
private static void callOnFinished(WeakReference<RenderNodeAnimator> weakThis) {
|
||||
RenderNodeAnimator animator = weakThis.get();
|
||||
if (animator != null) {
|
||||
animator.onFinished();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void finalize() throws Throwable {
|
||||
try {
|
||||
nUnref(mNativePtr);
|
||||
mNativePtr = 0;
|
||||
} finally {
|
||||
super.finalize();
|
||||
}
|
||||
}
|
||||
|
||||
private static native long nCreateAnimator(WeakReference<RenderNodeAnimator> weakThis,
|
||||
int property, int deltaValueType, float deltaValue);
|
||||
private static native void nSetDuration(long nativePtr, int duration);
|
||||
private static native void nUnref(long nativePtr);
|
||||
}
|
||||
@@ -50,7 +50,7 @@ import java.io.PrintWriter;
|
||||
public class ThreadedRenderer extends HardwareRenderer {
|
||||
private static final String LOGTAG = "ThreadedRenderer";
|
||||
|
||||
private static final Rect NULL_RECT = new Rect(-1, -1, -1, -1);
|
||||
private static final Rect NULL_RECT = new Rect();
|
||||
|
||||
private int mWidth, mHeight;
|
||||
private long mNativeProxy;
|
||||
@@ -58,9 +58,10 @@ public class ThreadedRenderer extends HardwareRenderer {
|
||||
private RenderNode mRootNode;
|
||||
|
||||
ThreadedRenderer(boolean translucent) {
|
||||
mNativeProxy = nCreateProxy(translucent);
|
||||
mRootNode = RenderNode.create("RootNode");
|
||||
long rootNodePtr = nCreateRootRenderNode();
|
||||
mRootNode = RenderNode.adopt(rootNodePtr);
|
||||
mRootNode.setClipToBounds(false);
|
||||
mNativeProxy = nCreateProxy(translucent, rootNodePtr);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,8 +203,7 @@ public class ThreadedRenderer extends HardwareRenderer {
|
||||
if (dirty == null) {
|
||||
dirty = NULL_RECT;
|
||||
}
|
||||
nDrawDisplayList(mNativeProxy, mRootNode.getNativeDisplayList(),
|
||||
dirty.left, dirty.top, dirty.right, dirty.bottom);
|
||||
nSyncAndDrawFrame(mNativeProxy, dirty.left, dirty.top, dirty.right, dirty.bottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -293,7 +293,8 @@ public class ThreadedRenderer extends HardwareRenderer {
|
||||
/** @hide */
|
||||
public static native void postToRenderThread(Runnable runnable);
|
||||
|
||||
private static native long nCreateProxy(boolean translucent);
|
||||
private static native long nCreateRootRenderNode();
|
||||
private static native long nCreateProxy(boolean translucent, long rootRenderNode);
|
||||
private static native void nDeleteProxy(long nativeProxy);
|
||||
|
||||
private static native boolean nInitialize(long nativeProxy, Surface window);
|
||||
@@ -302,7 +303,7 @@ public class ThreadedRenderer extends HardwareRenderer {
|
||||
private static native void nSetup(long nativeProxy, int width, int height);
|
||||
private static native void nSetDisplayListData(long nativeProxy, long displayList,
|
||||
long newData);
|
||||
private static native void nDrawDisplayList(long nativeProxy, long displayList,
|
||||
private static native void nSyncAndDrawFrame(long nativeProxy,
|
||||
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
|
||||
private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
|
||||
private static native void nDestroyCanvasAndSurface(long nativeProxy);
|
||||
|
||||
@@ -133,19 +133,19 @@ public class ViewPropertyAnimator {
|
||||
* Constants used to associate a property being requested and the mechanism used to set
|
||||
* the property (this class calls directly into View to set the properties in question).
|
||||
*/
|
||||
private static final int NONE = 0x0000;
|
||||
private static final int TRANSLATION_X = 0x0001;
|
||||
private static final int TRANSLATION_Y = 0x0002;
|
||||
private static final int TRANSLATION_Z = 0x0004;
|
||||
private static final int SCALE_X = 0x0008;
|
||||
private static final int SCALE_Y = 0x0010;
|
||||
private static final int ROTATION = 0x0020;
|
||||
private static final int ROTATION_X = 0x0040;
|
||||
private static final int ROTATION_Y = 0x0080;
|
||||
private static final int X = 0x0100;
|
||||
private static final int Y = 0x0200;
|
||||
private static final int Z = 0x0400;
|
||||
private static final int ALPHA = 0x0800;
|
||||
static final int NONE = 0x0000;
|
||||
static final int TRANSLATION_X = 0x0001;
|
||||
static final int TRANSLATION_Y = 0x0002;
|
||||
static final int TRANSLATION_Z = 0x0004;
|
||||
static final int SCALE_X = 0x0008;
|
||||
static final int SCALE_Y = 0x0010;
|
||||
static final int ROTATION = 0x0020;
|
||||
static final int ROTATION_X = 0x0040;
|
||||
static final int ROTATION_Y = 0x0080;
|
||||
static final int X = 0x0100;
|
||||
static final int Y = 0x0200;
|
||||
static final int Z = 0x0400;
|
||||
static final int ALPHA = 0x0800;
|
||||
|
||||
private static final int TRANSFORM_MASK = TRANSLATION_X | TRANSLATION_Y | TRANSLATION_Z |
|
||||
SCALE_X | SCALE_Y | ROTATION | ROTATION_X | ROTATION_Y | X | Y | Z;
|
||||
|
||||
@@ -61,6 +61,7 @@ LOCAL_SRC_FILES:= \
|
||||
android_view_MotionEvent.cpp \
|
||||
android_view_PointerIcon.cpp \
|
||||
android_view_RenderNode.cpp \
|
||||
android_view_RenderNodeAnimator.cpp \
|
||||
android_view_VelocityTracker.cpp \
|
||||
android_text_AndroidCharacter.cpp \
|
||||
android_text_AndroidBidi.cpp \
|
||||
|
||||
@@ -120,6 +120,7 @@ extern int register_android_graphics_Xfermode(JNIEnv* env);
|
||||
extern int register_android_graphics_pdf_PdfDocument(JNIEnv* env);
|
||||
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
|
||||
extern int register_android_view_RenderNode(JNIEnv* env);
|
||||
extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
|
||||
extern int register_android_view_GraphicBuffer(JNIEnv* env);
|
||||
extern int register_android_view_GLES20Canvas(JNIEnv* env);
|
||||
extern int register_android_view_GLRenderer(JNIEnv* env);
|
||||
@@ -1193,6 +1194,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_graphics_Graphics),
|
||||
REG_JNI(register_android_view_DisplayEventReceiver),
|
||||
REG_JNI(register_android_view_RenderNode),
|
||||
REG_JNI(register_android_view_RenderNodeAnimator),
|
||||
REG_JNI(register_android_view_GraphicBuffer),
|
||||
REG_JNI(register_android_view_GLES20Canvas),
|
||||
REG_JNI(register_android_view_GLRenderer),
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include <Animator.h>
|
||||
#include <DisplayListRenderer.h>
|
||||
#include <RenderNode.h>
|
||||
|
||||
@@ -438,6 +439,25 @@ static jfloat android_view_RenderNode_getPivotY(JNIEnv* env,
|
||||
return renderNode->stagingProperties().getPivotY();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// RenderProperties - Animations
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static void android_view_RenderNode_addAnimator(JNIEnv* env, jobject clazz,
|
||||
jlong renderNodePtr, jlong animatorPtr) {
|
||||
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
|
||||
RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
|
||||
renderNode->addAnimator(animator);
|
||||
}
|
||||
|
||||
static void android_view_RenderNode_removeAnimator(JNIEnv* env, jobject clazz,
|
||||
jlong renderNodePtr, jlong animatorPtr) {
|
||||
RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
|
||||
RenderPropertyAnimator* animator = reinterpret_cast<RenderPropertyAnimator*>(animatorPtr);
|
||||
renderNode->removeAnimator(animator);
|
||||
}
|
||||
|
||||
|
||||
#endif // USE_OPENGL_RENDERER
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -513,6 +533,9 @@ static JNINativeMethod gMethods[] = {
|
||||
|
||||
{ "nGetPivotX", "(J)F", (void*) android_view_RenderNode_getPivotX },
|
||||
{ "nGetPivotY", "(J)F", (void*) android_view_RenderNode_getPivotY },
|
||||
|
||||
{ "nAddAnimator", "(JJ)V", (void*) android_view_RenderNode_addAnimator },
|
||||
{ "nRemoveAnimator", "(JJ)V", (void*) android_view_RenderNode_removeAnimator },
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
127
core/jni/android_view_RenderNodeAnimator.cpp
Normal file
127
core/jni/android_view_RenderNodeAnimator.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
|
||||
#include "android_view_RenderNodeAnimator.h"
|
||||
|
||||
#include "jni.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
|
||||
#include <Animator.h>
|
||||
#include <Interpolator.h>
|
||||
#include <RenderProperties.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
using namespace uirenderer;
|
||||
|
||||
static struct {
|
||||
jclass clazz;
|
||||
|
||||
jmethodID callOnFinished;
|
||||
} gRenderNodeAnimatorClassInfo;
|
||||
|
||||
static JNIEnv* getEnv(JavaVM* vm) {
|
||||
JNIEnv* env;
|
||||
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
|
||||
return 0;
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
RenderNodeAnimator::RenderNodeAnimator(JNIEnv* env, jobject weakThis,
|
||||
RenderProperty property, DeltaValueType deltaType, float delta)
|
||||
: RenderPropertyAnimator(property, deltaType, delta) {
|
||||
mWeakThis = env->NewGlobalRef(weakThis);
|
||||
env->GetJavaVM(&mJvm);
|
||||
}
|
||||
|
||||
RenderNodeAnimator::~RenderNodeAnimator() {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->DeleteGlobalRef(mWeakThis);
|
||||
mWeakThis = NULL;
|
||||
}
|
||||
|
||||
void RenderNodeAnimator::callOnFinished() {
|
||||
JNIEnv* env = getEnv(mJvm);
|
||||
env->CallStaticVoidMethod(
|
||||
gRenderNodeAnimatorClassInfo.clazz,
|
||||
gRenderNodeAnimatorClassInfo.callOnFinished,
|
||||
mWeakThis);
|
||||
}
|
||||
|
||||
static jlong createAnimator(JNIEnv* env, jobject clazz, jobject weakThis,
|
||||
jint property, jint deltaType, jfloat deltaValue) {
|
||||
LOG_ALWAYS_FATAL_IF(property < 0 || property > RenderNodeAnimator::ALPHA,
|
||||
"Invalid property %d", property);
|
||||
LOG_ALWAYS_FATAL_IF(deltaType != RenderPropertyAnimator::DELTA
|
||||
&& deltaType != RenderPropertyAnimator::ABSOLUTE,
|
||||
"Invalid delta type %d", deltaType);
|
||||
|
||||
RenderNodeAnimator* animator = new RenderNodeAnimator(env, weakThis,
|
||||
static_cast<RenderPropertyAnimator::RenderProperty>(property),
|
||||
static_cast<RenderPropertyAnimator::DeltaValueType>(deltaType),
|
||||
deltaValue);
|
||||
animator->incStrong(0);
|
||||
return reinterpret_cast<jlong>( animator );
|
||||
}
|
||||
|
||||
static void setDuration(JNIEnv* env, jobject clazz, jlong animatorPtr, jint duration) {
|
||||
LOG_ALWAYS_FATAL_IF(duration < 0, "Duration cannot be negative");
|
||||
RenderNodeAnimator* animator = reinterpret_cast<RenderNodeAnimator*>(animatorPtr);
|
||||
animator->setDuration(duration);
|
||||
}
|
||||
|
||||
static void unref(JNIEnv* env, jobject clazz, jlong objPtr) {
|
||||
VirtualLightRefBase* obj = reinterpret_cast<VirtualLightRefBase*>(objPtr);
|
||||
obj->decStrong(0);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// JNI Glue
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
const char* const kClassPathName = "android/view/RenderNodeAnimator";
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
{ "nCreateAnimator", "(Ljava/lang/ref/WeakReference;IIF)J", (void*) createAnimator },
|
||||
{ "nSetDuration", "(JI)V", (void*) setDuration },
|
||||
{ "nUnref", "(J)V", (void*) unref },
|
||||
};
|
||||
|
||||
#define FIND_CLASS(var, className) \
|
||||
var = env->FindClass(className); \
|
||||
LOG_FATAL_IF(! var, "Unable to find class " className);
|
||||
|
||||
#define GET_STATIC_METHOD_ID(var, clazz, methodName, methodDescriptor) \
|
||||
var = env->GetStaticMethodID(clazz, methodName, methodDescriptor); \
|
||||
LOG_FATAL_IF(! var, "Unable to find method " methodName);
|
||||
|
||||
int register_android_view_RenderNodeAnimator(JNIEnv* env) {
|
||||
FIND_CLASS(gRenderNodeAnimatorClassInfo.clazz, kClassPathName);
|
||||
gRenderNodeAnimatorClassInfo.clazz = jclass(env->NewGlobalRef(gRenderNodeAnimatorClassInfo.clazz));
|
||||
|
||||
GET_STATIC_METHOD_ID(gRenderNodeAnimatorClassInfo.callOnFinished, gRenderNodeAnimatorClassInfo.clazz,
|
||||
"callOnFinished", "(Ljava/lang/ref/WeakReference;)V");
|
||||
|
||||
return AndroidRuntime::registerNativeMethods(env, kClassPathName, gMethods, NELEM(gMethods));
|
||||
}
|
||||
|
||||
|
||||
} // namespace android
|
||||
36
core/jni/android_view_RenderNodeAnimator.h
Normal file
36
core/jni/android_view_RenderNodeAnimator.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2012 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.
|
||||
*/
|
||||
|
||||
#include "jni.h"
|
||||
|
||||
#include <Animator.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class RenderNodeAnimator : public uirenderer::RenderPropertyAnimator {
|
||||
public:
|
||||
RenderNodeAnimator(JNIEnv* env, jobject callbackObject,
|
||||
RenderProperty property, DeltaValueType deltaType, float delta);
|
||||
virtual ~RenderNodeAnimator();
|
||||
|
||||
void callOnFinished();
|
||||
|
||||
private:
|
||||
JavaVM* mJvm;
|
||||
jobject mWeakThis;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#define LOG_TAG "ThreadedRenderer"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "jni.h"
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
@@ -24,6 +26,8 @@
|
||||
#include <android_runtime/android_view_Surface.h>
|
||||
#include <system/window.h>
|
||||
|
||||
#include "android_view_RenderNodeAnimator.h"
|
||||
#include <RenderNode.h>
|
||||
#include <renderthread/RenderProxy.h>
|
||||
#include <renderthread/RenderTask.h>
|
||||
#include <renderthread/RenderThread.h>
|
||||
@@ -63,15 +67,75 @@ private:
|
||||
jobject mRunnable;
|
||||
};
|
||||
|
||||
class InvokeAnimationListeners : public MessageHandler {
|
||||
public:
|
||||
InvokeAnimationListeners(std::vector< sp<RenderNodeAnimator> >& animators) {
|
||||
mAnimators.swap(animators);
|
||||
}
|
||||
|
||||
static void callOnFinished(const sp<RenderNodeAnimator>& animator) {
|
||||
animator->callOnFinished();
|
||||
}
|
||||
|
||||
virtual void handleMessage(const Message& message) {
|
||||
std::for_each(mAnimators.begin(), mAnimators.end(), callOnFinished);
|
||||
mAnimators.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector< sp<RenderNodeAnimator> > mAnimators;
|
||||
};
|
||||
|
||||
class RootRenderNode : public RenderNode, public AnimationListener {
|
||||
public:
|
||||
RootRenderNode() : RenderNode() {
|
||||
mLooper = Looper::getForThread();
|
||||
LOG_ALWAYS_FATAL_IF(!mLooper.get(),
|
||||
"Must create RootRenderNode on a thread with a looper!");
|
||||
}
|
||||
|
||||
virtual ~RootRenderNode() {}
|
||||
|
||||
void onAnimationFinished(const sp<RenderPropertyAnimator>& animator) {
|
||||
mFinishedAnimators.push_back(
|
||||
reinterpret_cast<RenderNodeAnimator*>(animator.get()));
|
||||
}
|
||||
|
||||
virtual void prepareTree(TreeInfo& info) {
|
||||
info.animationListener = this;
|
||||
RenderNode::prepareTree(info);
|
||||
info.animationListener = NULL;
|
||||
|
||||
// post all the finished stuff
|
||||
if (mFinishedAnimators.size()) {
|
||||
sp<InvokeAnimationListeners> message
|
||||
= new InvokeAnimationListeners(mFinishedAnimators);
|
||||
mLooper->sendMessage(message, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sp<Looper> mLooper;
|
||||
std::vector< sp<RenderNodeAnimator> > mFinishedAnimators;
|
||||
};
|
||||
|
||||
static void android_view_ThreadedRenderer_postToRenderThread(JNIEnv* env, jobject clazz,
|
||||
jobject jrunnable) {
|
||||
RenderTask* task = new JavaTask(env, jrunnable);
|
||||
RenderThread::getInstance().queue(task);
|
||||
}
|
||||
|
||||
static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
|
||||
RootRenderNode* node = new RootRenderNode();
|
||||
node->incStrong(0);
|
||||
node->setName("RootRenderNode");
|
||||
return reinterpret_cast<jlong>(node);
|
||||
}
|
||||
|
||||
static jlong android_view_ThreadedRenderer_createProxy(JNIEnv* env, jobject clazz,
|
||||
jboolean translucent) {
|
||||
return (jlong) new RenderProxy(translucent);
|
||||
jboolean translucent, jlong rootRenderNodePtr) {
|
||||
RenderNode* rootRenderNode = reinterpret_cast<RenderNode*>(rootRenderNodePtr);
|
||||
return (jlong) new RenderProxy(translucent, rootRenderNode);
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_deleteProxy(JNIEnv* env, jobject clazz,
|
||||
@@ -113,12 +177,11 @@ static void android_view_ThreadedRenderer_setup(JNIEnv* env, jobject clazz,
|
||||
proxy->setup(width, height);
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_drawDisplayList(JNIEnv* env, jobject clazz,
|
||||
jlong proxyPtr, jlong displayListPtr, jint dirtyLeft, jint dirtyTop,
|
||||
static void android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
|
||||
jlong proxyPtr, jint dirtyLeft, jint dirtyTop,
|
||||
jint dirtyRight, jint dirtyBottom) {
|
||||
RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
|
||||
RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
|
||||
proxy->drawDisplayList(displayList, dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
|
||||
proxy->syncAndDrawFrame(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
|
||||
}
|
||||
|
||||
static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz,
|
||||
@@ -187,13 +250,14 @@ const char* const kClassPathName = "android/view/ThreadedRenderer";
|
||||
static JNINativeMethod gMethods[] = {
|
||||
#ifdef USE_OPENGL_RENDERER
|
||||
{ "postToRenderThread", "(Ljava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_postToRenderThread },
|
||||
{ "nCreateProxy", "(Z)J", (void*) android_view_ThreadedRenderer_createProxy },
|
||||
{ "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
|
||||
{ "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
|
||||
{ "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
|
||||
{ "nInitialize", "(JLandroid/view/Surface;)Z", (void*) android_view_ThreadedRenderer_initialize },
|
||||
{ "nUpdateSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_updateSurface },
|
||||
{ "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface },
|
||||
{ "nSetup", "(JII)V", (void*) android_view_ThreadedRenderer_setup },
|
||||
{ "nDrawDisplayList", "(JJIIII)V", (void*) android_view_ThreadedRenderer_drawDisplayList },
|
||||
{ "nSyncAndDrawFrame", "(JIIII)V", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
|
||||
{ "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
|
||||
{ "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
|
||||
{ "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
|
||||
|
||||
@@ -11,6 +11,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
|
||||
font/CacheTexture.cpp \
|
||||
font/Font.cpp \
|
||||
AmbientShadow.cpp \
|
||||
Animator.cpp \
|
||||
AssetAtlas.cpp \
|
||||
FontRenderer.cpp \
|
||||
GammaFontRenderer.cpp \
|
||||
@@ -25,6 +26,7 @@ ifeq ($(USE_OPENGL_RENDERER),true)
|
||||
FboCache.cpp \
|
||||
GradientCache.cpp \
|
||||
Image.cpp \
|
||||
Interpolator.cpp \
|
||||
Layer.cpp \
|
||||
LayerCache.cpp \
|
||||
LayerRenderer.cpp \
|
||||
@@ -66,6 +68,8 @@ ifeq ($(USE_OPENGL_RENDERER),true)
|
||||
$(LOCAL_PATH)/../../include/utils \
|
||||
external/skia/src/core
|
||||
|
||||
include external/stlport/libstlport.mk
|
||||
|
||||
LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
|
||||
LOCAL_CFLAGS += -Wno-unused-parameter
|
||||
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
|
||||
|
||||
240
libs/hwui/Animator.cpp
Normal file
240
libs/hwui/Animator.cpp
Normal file
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "RT-Animator"
|
||||
|
||||
#include "Animator.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "RenderProperties.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
/************************************************************
|
||||
* Private header
|
||||
************************************************************/
|
||||
|
||||
typedef void (RenderProperties::*SetFloatProperty)(float value);
|
||||
typedef float (RenderProperties::*GetFloatProperty)() const;
|
||||
|
||||
struct PropertyAccessors {
|
||||
GetFloatProperty getter;
|
||||
SetFloatProperty setter;
|
||||
};
|
||||
|
||||
// Maps RenderProperty enum to accessors
|
||||
static const PropertyAccessors PROPERTY_ACCESSOR_LUT[] = {
|
||||
{&RenderProperties::getTranslationX, &RenderProperties::setTranslationX },
|
||||
{&RenderProperties::getTranslationY, &RenderProperties::setTranslationY },
|
||||
{&RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ },
|
||||
{&RenderProperties::getScaleX, &RenderProperties::setScaleX },
|
||||
{&RenderProperties::getScaleY, &RenderProperties::setScaleY },
|
||||
{&RenderProperties::getRotation, &RenderProperties::setRotation },
|
||||
{&RenderProperties::getRotationX, &RenderProperties::setRotationX },
|
||||
{&RenderProperties::getRotationY, &RenderProperties::setRotationY },
|
||||
{&RenderProperties::getX, &RenderProperties::setX },
|
||||
{&RenderProperties::getY, &RenderProperties::setY },
|
||||
{&RenderProperties::getZ, &RenderProperties::setZ },
|
||||
{&RenderProperties::getAlpha, &RenderProperties::setAlpha },
|
||||
};
|
||||
|
||||
// Helper class to contain generic animator helpers
|
||||
class BaseAnimator {
|
||||
public:
|
||||
BaseAnimator();
|
||||
virtual ~BaseAnimator();
|
||||
|
||||
void setInterpolator(Interpolator* interpolator);
|
||||
void setDuration(nsecs_t durationInMs);
|
||||
|
||||
bool isFinished() { return mPlayState == FINISHED; }
|
||||
|
||||
protected:
|
||||
// This is the main animation entrypoint that subclasses should call
|
||||
// to generate the onAnimation* lifecycle events
|
||||
// Returns true if the animation has finished, false otherwise
|
||||
bool animateFrame(nsecs_t frameTime);
|
||||
|
||||
// Called when PlayState switches from PENDING to RUNNING
|
||||
virtual void onAnimationStarted() {}
|
||||
virtual void onAnimationUpdated(float fraction) = 0;
|
||||
virtual void onAnimationFinished() {}
|
||||
|
||||
private:
|
||||
enum PlayState {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
FINISHED,
|
||||
};
|
||||
|
||||
Interpolator* mInterpolator;
|
||||
PlayState mPlayState;
|
||||
long mStartTime;
|
||||
long mDuration;
|
||||
};
|
||||
|
||||
// Hide the base classes & private bits from the exported RenderPropertyAnimator
|
||||
// in this Impl class so that subclasses of RenderPropertyAnimator don't require
|
||||
// knowledge of the inner guts but only the public virtual methods.
|
||||
// Animates a single property
|
||||
class RenderPropertyAnimatorImpl : public BaseAnimator {
|
||||
public:
|
||||
RenderPropertyAnimatorImpl(GetFloatProperty getter, SetFloatProperty setter,
|
||||
RenderPropertyAnimator::DeltaValueType deltaType, float delta);
|
||||
~RenderPropertyAnimatorImpl();
|
||||
|
||||
bool animate(RenderProperties* target, TreeInfo& info);
|
||||
|
||||
protected:
|
||||
virtual void onAnimationStarted();
|
||||
virtual void onAnimationUpdated(float fraction);
|
||||
|
||||
private:
|
||||
// mTarget is only valid inside animate()
|
||||
RenderProperties* mTarget;
|
||||
GetFloatProperty mGetter;
|
||||
SetFloatProperty mSetter;
|
||||
|
||||
RenderPropertyAnimator::DeltaValueType mDeltaValueType;
|
||||
float mDeltaValue;
|
||||
float mFromValue;
|
||||
};
|
||||
|
||||
RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property,
|
||||
DeltaValueType deltaType, float deltaValue) {
|
||||
PropertyAccessors pa = PROPERTY_ACCESSOR_LUT[property];
|
||||
mImpl = new RenderPropertyAnimatorImpl(pa.getter, pa.setter, deltaType, deltaValue);
|
||||
}
|
||||
|
||||
RenderPropertyAnimator::~RenderPropertyAnimator() {
|
||||
delete mImpl;
|
||||
mImpl = NULL;
|
||||
}
|
||||
|
||||
void RenderPropertyAnimator::setInterpolator(Interpolator* interpolator) {
|
||||
mImpl->setInterpolator(interpolator);
|
||||
}
|
||||
|
||||
void RenderPropertyAnimator::setDuration(nsecs_t durationInMs) {
|
||||
mImpl->setDuration(durationInMs);
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimator::isFinished() {
|
||||
return mImpl->isFinished();
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimator::animate(RenderProperties* target, TreeInfo& info) {
|
||||
return mImpl->animate(target, info);
|
||||
}
|
||||
|
||||
|
||||
/************************************************************
|
||||
* Base animator
|
||||
************************************************************/
|
||||
|
||||
BaseAnimator::BaseAnimator()
|
||||
: mInterpolator(0)
|
||||
, mPlayState(PENDING)
|
||||
, mStartTime(0)
|
||||
, mDuration(300) {
|
||||
|
||||
}
|
||||
|
||||
BaseAnimator::~BaseAnimator() {
|
||||
setInterpolator(NULL);
|
||||
}
|
||||
|
||||
void BaseAnimator::setInterpolator(Interpolator* interpolator) {
|
||||
delete mInterpolator;
|
||||
mInterpolator = interpolator;
|
||||
}
|
||||
|
||||
void BaseAnimator::setDuration(nsecs_t duration) {
|
||||
mDuration = duration;
|
||||
}
|
||||
|
||||
bool BaseAnimator::animateFrame(nsecs_t frameTime) {
|
||||
if (mPlayState == PENDING) {
|
||||
mPlayState = RUNNING;
|
||||
mStartTime = frameTime;
|
||||
// No interpolator was set, use the default
|
||||
if (!mInterpolator) {
|
||||
setInterpolator(Interpolator::createDefaultInterpolator());
|
||||
}
|
||||
onAnimationStarted();
|
||||
}
|
||||
|
||||
float fraction = 1.0f;
|
||||
if (mPlayState == RUNNING) {
|
||||
fraction = mDuration > 0 ? (float)(frameTime - mStartTime) / mDuration : 1.0f;
|
||||
if (fraction >= 1.0f) {
|
||||
fraction = 1.0f;
|
||||
mPlayState = FINISHED;
|
||||
}
|
||||
}
|
||||
fraction = mInterpolator->interpolate(fraction);
|
||||
onAnimationUpdated(fraction);
|
||||
|
||||
if (mPlayState == FINISHED) {
|
||||
onAnimationFinished();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/************************************************************
|
||||
* RenderPropertyAnimator
|
||||
************************************************************/
|
||||
|
||||
RenderPropertyAnimatorImpl::RenderPropertyAnimatorImpl(
|
||||
GetFloatProperty getter, SetFloatProperty setter,
|
||||
RenderPropertyAnimator::DeltaValueType deltaType, float delta)
|
||||
: mTarget(0)
|
||||
, mGetter(getter)
|
||||
, mSetter(setter)
|
||||
, mDeltaValueType(deltaType)
|
||||
, mDeltaValue(delta)
|
||||
, mFromValue(-1) {
|
||||
}
|
||||
|
||||
RenderPropertyAnimatorImpl::~RenderPropertyAnimatorImpl() {
|
||||
}
|
||||
|
||||
bool RenderPropertyAnimatorImpl::animate(RenderProperties* target, TreeInfo& info) {
|
||||
mTarget = target;
|
||||
bool finished = animateFrame(info.frameTimeMs);
|
||||
mTarget = NULL;
|
||||
return finished;
|
||||
}
|
||||
|
||||
void RenderPropertyAnimatorImpl::onAnimationStarted() {
|
||||
mFromValue = (mTarget->*mGetter)();
|
||||
|
||||
if (mDeltaValueType == RenderPropertyAnimator::ABSOLUTE) {
|
||||
mDeltaValue = (mDeltaValue - mFromValue);
|
||||
mDeltaValueType = RenderPropertyAnimator::DELTA;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPropertyAnimatorImpl::onAnimationUpdated(float fraction) {
|
||||
float value = mFromValue + (mDeltaValue * fraction);
|
||||
(mTarget->*mSetter)(value);
|
||||
}
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
79
libs/hwui/Animator.h
Normal file
79
libs/hwui/Animator.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef ANIMATOR_H
|
||||
#define ANIMATOR_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
|
||||
#include "Interpolator.h"
|
||||
#include "TreeInfo.h"
|
||||
#include "utils/VirtualLightRefBase.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class RenderProperties;
|
||||
class RenderPropertyAnimatorImpl;
|
||||
|
||||
class RenderPropertyAnimator : public VirtualLightRefBase {
|
||||
public:
|
||||
// Since the UI thread doesn't necessarily know what the current values
|
||||
// actually are and thus can't do the calculations, this is used to inform
|
||||
// the animator how to lazy-resolve the input value
|
||||
enum DeltaValueType {
|
||||
// The delta value represents an absolute value endpoint
|
||||
// mDeltaValue needs to be recalculated to be mDelta = (mDelta - fromValue)
|
||||
// in onAnimationStarted()
|
||||
ABSOLUTE = 0,
|
||||
// The final value represents an offset from the current value
|
||||
// No recalculation is needed
|
||||
DELTA,
|
||||
};
|
||||
|
||||
enum RenderProperty {
|
||||
TRANSLATION_X = 0,
|
||||
TRANSLATION_Y,
|
||||
TRANSLATION_Z,
|
||||
SCALE_X,
|
||||
SCALE_Y,
|
||||
ROTATION,
|
||||
ROTATION_X,
|
||||
ROTATION_Y,
|
||||
X,
|
||||
Y,
|
||||
Z,
|
||||
ALPHA,
|
||||
};
|
||||
|
||||
ANDROID_API void setInterpolator(Interpolator* interpolator);
|
||||
ANDROID_API void setDuration(nsecs_t durationInMs);
|
||||
ANDROID_API bool isFinished();
|
||||
|
||||
bool animate(RenderProperties* target, TreeInfo& info);
|
||||
|
||||
protected:
|
||||
ANDROID_API RenderPropertyAnimator(RenderProperty property, DeltaValueType deltaType,
|
||||
float deltaValue);
|
||||
ANDROID_API virtual ~RenderPropertyAnimator();
|
||||
|
||||
private:
|
||||
RenderPropertyAnimatorImpl* mImpl;
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* ANIMATOR_H */
|
||||
32
libs/hwui/Interpolator.cpp
Normal file
32
libs/hwui/Interpolator.cpp
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#include "Interpolator.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
Interpolator* Interpolator::createDefaultInterpolator() {
|
||||
return new AccelerateDecelerateInterpolator();
|
||||
}
|
||||
|
||||
float AccelerateDecelerateInterpolator::interpolate(float input) {
|
||||
return (float)(cosf((input + 1) * M_PI) / 2.0f) + 0.5f;
|
||||
}
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
45
libs/hwui/Interpolator.h
Normal file
45
libs/hwui/Interpolator.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef INTERPOLATOR_H
|
||||
#define INTERPOLATOR_H
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class Interpolator {
|
||||
public:
|
||||
virtual ~Interpolator() {}
|
||||
|
||||
virtual float interpolate(float input) = 0;
|
||||
|
||||
static Interpolator* createDefaultInterpolator();
|
||||
|
||||
protected:
|
||||
Interpolator() {}
|
||||
};
|
||||
|
||||
class AccelerateDecelerateInterpolator : public Interpolator {
|
||||
public:
|
||||
AccelerateDecelerateInterpolator() {}
|
||||
virtual ~AccelerateDecelerateInterpolator() {}
|
||||
|
||||
virtual float interpolate(float input);
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* INTERPOLATOR_H */
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "RenderNode.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <SkCanvas.h>
|
||||
#include <algorithm>
|
||||
|
||||
@@ -54,7 +56,8 @@ RenderNode::RenderNode()
|
||||
: mNeedsPropertiesSync(false)
|
||||
, mNeedsDisplayListDataSync(false)
|
||||
, mDisplayListData(0)
|
||||
, mStagingDisplayListData(0) {
|
||||
, mStagingDisplayListData(0)
|
||||
, mNeedsAnimatorsSync(false) {
|
||||
}
|
||||
|
||||
RenderNode::~RenderNode() {
|
||||
@@ -97,15 +100,32 @@ void RenderNode::prepareTree(TreeInfo& info) {
|
||||
}
|
||||
|
||||
void RenderNode::prepareTreeImpl(TreeInfo& info) {
|
||||
pushStagingChanges(info);
|
||||
if (info.performStagingPush) {
|
||||
pushStagingChanges(info);
|
||||
}
|
||||
if (info.evaluateAnimations) {
|
||||
evaluateAnimations(info);
|
||||
}
|
||||
prepareSubTree(info, mDisplayListData);
|
||||
}
|
||||
|
||||
static bool is_finished(const sp<RenderPropertyAnimator>& animator) {
|
||||
return animator->isFinished();
|
||||
}
|
||||
|
||||
void RenderNode::pushStagingChanges(TreeInfo& info) {
|
||||
if (mNeedsPropertiesSync) {
|
||||
mNeedsPropertiesSync = false;
|
||||
mProperties = mStagingProperties;
|
||||
}
|
||||
if (mNeedsAnimatorsSync) {
|
||||
mAnimators.reserve(mStagingAnimators.size());
|
||||
std::vector< sp<RenderPropertyAnimator> >::iterator it;
|
||||
// hint: this means copy_if_not()
|
||||
it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(),
|
||||
mAnimators.begin(), is_finished);
|
||||
mAnimators.resize(std::distance(mAnimators.begin(), it));
|
||||
}
|
||||
if (mNeedsDisplayListDataSync) {
|
||||
mNeedsDisplayListDataSync = false;
|
||||
// Do a push pass on the old tree to handle freeing DisplayListData
|
||||
@@ -119,6 +139,34 @@ void RenderNode::pushStagingChanges(TreeInfo& info) {
|
||||
}
|
||||
}
|
||||
|
||||
class AnimateFunctor {
|
||||
public:
|
||||
AnimateFunctor(RenderProperties* target, TreeInfo& info)
|
||||
: mTarget(target), mInfo(info) {}
|
||||
|
||||
bool operator() (sp<RenderPropertyAnimator>& animator) {
|
||||
bool finished = animator->animate(mTarget, mInfo);
|
||||
if (finished && mInfo.animationListener) {
|
||||
mInfo.animationListener->onAnimationFinished(animator);
|
||||
}
|
||||
return finished;
|
||||
}
|
||||
private:
|
||||
RenderProperties* mTarget;
|
||||
TreeInfo& mInfo;
|
||||
};
|
||||
|
||||
void RenderNode::evaluateAnimations(TreeInfo& info) {
|
||||
if (!mAnimators.size()) return;
|
||||
|
||||
AnimateFunctor functor(&mProperties, info);
|
||||
std::vector< sp<RenderPropertyAnimator> >::iterator newEnd;
|
||||
newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor);
|
||||
mAnimators.erase(newEnd, mAnimators.end());
|
||||
mProperties.updateMatrix();
|
||||
info.hasAnimations |= mAnimators.size();
|
||||
}
|
||||
|
||||
void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) {
|
||||
if (subtree) {
|
||||
TextureCache& cache = Caches::getInstance().textureCache;
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
#endif
|
||||
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <SkCamera.h>
|
||||
#include <SkMatrix.h>
|
||||
|
||||
@@ -41,6 +44,7 @@
|
||||
#include "DeferredDisplayList.h"
|
||||
#include "DisplayList.h"
|
||||
#include "RenderProperties.h"
|
||||
#include "TreeInfo.h"
|
||||
#include "utils/VirtualLightRefBase.h"
|
||||
|
||||
class SkBitmap;
|
||||
@@ -65,17 +69,6 @@ class SaveOp;
|
||||
class RestoreToCountOp;
|
||||
class DrawDisplayListOp;
|
||||
|
||||
struct TreeInfo {
|
||||
TreeInfo()
|
||||
: hasFunctors(false)
|
||||
, prepareTextures(false)
|
||||
{}
|
||||
|
||||
bool hasFunctors;
|
||||
bool prepareTextures;
|
||||
// TODO: Damage calculations? Flag to skip staging pushes for RT animations?
|
||||
};
|
||||
|
||||
/**
|
||||
* Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
|
||||
*
|
||||
@@ -91,7 +84,7 @@ struct TreeInfo {
|
||||
class RenderNode : public VirtualLightRefBase {
|
||||
public:
|
||||
ANDROID_API RenderNode();
|
||||
ANDROID_API ~RenderNode();
|
||||
ANDROID_API virtual ~RenderNode();
|
||||
|
||||
// See flags defined in DisplayList.java
|
||||
enum ReplayFlag {
|
||||
@@ -152,7 +145,19 @@ public:
|
||||
return properties().getHeight();
|
||||
}
|
||||
|
||||
ANDROID_API void prepareTree(TreeInfo& info);
|
||||
ANDROID_API virtual void prepareTree(TreeInfo& info);
|
||||
|
||||
// UI thread only!
|
||||
ANDROID_API void addAnimator(const sp<RenderPropertyAnimator>& animator) {
|
||||
mStagingAnimators.insert(animator);
|
||||
mNeedsAnimatorsSync = true;
|
||||
}
|
||||
|
||||
// UI thread only!
|
||||
ANDROID_API void removeAnimator(const sp<RenderPropertyAnimator>& animator) {
|
||||
mStagingAnimators.erase(animator);
|
||||
mNeedsAnimatorsSync = true;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
|
||||
@@ -214,6 +219,7 @@ private:
|
||||
|
||||
void prepareTreeImpl(TreeInfo& info);
|
||||
void pushStagingChanges(TreeInfo& info);
|
||||
void evaluateAnimations(TreeInfo& info);
|
||||
void prepareSubTree(TreeInfo& info, DisplayListData* subtree);
|
||||
|
||||
String8 mName;
|
||||
@@ -226,6 +232,10 @@ private:
|
||||
DisplayListData* mDisplayListData;
|
||||
DisplayListData* mStagingDisplayListData;
|
||||
|
||||
bool mNeedsAnimatorsSync;
|
||||
std::set< sp<RenderPropertyAnimator> > mStagingAnimators;
|
||||
std::vector< sp<RenderPropertyAnimator> > mAnimators;
|
||||
|
||||
/**
|
||||
* Draw time state - these properties are only set and used during rendering
|
||||
*/
|
||||
|
||||
@@ -51,7 +51,8 @@ RenderProperties::PrimitiveFields::PrimitiveFields()
|
||||
|
||||
RenderProperties::ComputedFields::ComputedFields()
|
||||
: mTransformMatrix(NULL)
|
||||
, mClipPath(NULL) {
|
||||
, mClipPath(NULL)
|
||||
, mClipPathOp(SkRegion::kIntersect_Op) {
|
||||
}
|
||||
|
||||
RenderProperties::ComputedFields::~ComputedFields() {
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
#ifndef RENDERNODEPROPERTIES_H
|
||||
#define RENDERNODEPROPERTIES_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <stddef.h>
|
||||
#include <vector>
|
||||
#include <cutils/compiler.h>
|
||||
#include <androidfw/ResourceTypes.h>
|
||||
|
||||
@@ -24,6 +26,7 @@
|
||||
#include <SkMatrix.h>
|
||||
#include <SkRegion.h>
|
||||
|
||||
#include "Animator.h"
|
||||
#include "Rect.h"
|
||||
#include "RevealClip.h"
|
||||
#include "Outline.h"
|
||||
@@ -149,6 +152,31 @@ public:
|
||||
return mPrimitiveFields.mTranslationZ;
|
||||
}
|
||||
|
||||
// Animation helper
|
||||
void setX(float value) {
|
||||
setTranslationX(value - getLeft());
|
||||
}
|
||||
|
||||
// Animation helper
|
||||
float getX() const {
|
||||
return getLeft() + getTranslationX();
|
||||
}
|
||||
|
||||
// Animation helper
|
||||
void setY(float value) {
|
||||
setTranslationY(value - getTop());
|
||||
}
|
||||
|
||||
// Animation helper
|
||||
float getY() const {
|
||||
return getTop() + getTranslationY();
|
||||
}
|
||||
|
||||
// Animation helper
|
||||
void setZ(float value) {
|
||||
setTranslationZ(value - getElevation());
|
||||
}
|
||||
|
||||
float getZ() const {
|
||||
return getElevation() + getTranslationZ();
|
||||
}
|
||||
@@ -457,7 +485,6 @@ private:
|
||||
bool mCaching;
|
||||
} mPrimitiveFields;
|
||||
|
||||
// mCameraDistance isn't in mPrimitiveFields as it has a complex setter
|
||||
SkMatrix* mStaticMatrix;
|
||||
SkMatrix* mAnimationMatrix;
|
||||
|
||||
|
||||
64
libs/hwui/TreeInfo.h
Normal file
64
libs/hwui/TreeInfo.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef TREEINFO_H
|
||||
#define TREEINFO_H
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Timers.h>
|
||||
#include <utils/StrongPointer.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class RenderPropertyAnimator;
|
||||
|
||||
class AnimationListener {
|
||||
public:
|
||||
ANDROID_API virtual void onAnimationFinished(const sp<RenderPropertyAnimator>&) = 0;
|
||||
protected:
|
||||
ANDROID_API virtual ~AnimationListener() {}
|
||||
};
|
||||
|
||||
struct TreeInfo {
|
||||
// The defaults here should be safe for everyone but DrawFrameTask to use as-is.
|
||||
TreeInfo()
|
||||
: hasFunctors(false)
|
||||
, prepareTextures(false)
|
||||
, performStagingPush(true)
|
||||
, frameTimeMs(0)
|
||||
, evaluateAnimations(false)
|
||||
, hasAnimations(false)
|
||||
, animationListener(0)
|
||||
{}
|
||||
|
||||
bool hasFunctors;
|
||||
bool prepareTextures;
|
||||
bool performStagingPush;
|
||||
|
||||
// Animations
|
||||
nsecs_t frameTimeMs;
|
||||
bool evaluateAnimations;
|
||||
// This is only updated if evaluateAnimations is true
|
||||
bool hasAnimations;
|
||||
AnimationListener* animationListener;
|
||||
|
||||
// TODO: Damage calculations
|
||||
};
|
||||
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
#endif /* TREEINFO_H */
|
||||
@@ -308,18 +308,20 @@ bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
|
||||
return value == EGL_BUFFER_PRESERVED;
|
||||
}
|
||||
|
||||
CanvasContext::CanvasContext(bool translucent)
|
||||
CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
|
||||
: mRenderThread(RenderThread::getInstance())
|
||||
, mEglSurface(EGL_NO_SURFACE)
|
||||
, mDirtyRegionsEnabled(false)
|
||||
, mOpaque(!translucent)
|
||||
, mCanvas(0)
|
||||
, mHaveNewSurface(false) {
|
||||
, mHaveNewSurface(false)
|
||||
, mRootRenderNode(rootRenderNode) {
|
||||
mGlobalContext = GlobalContext::get();
|
||||
}
|
||||
|
||||
CanvasContext::~CanvasContext() {
|
||||
destroyCanvasAndSurface();
|
||||
mRenderThread.removeFrameCallback(this);
|
||||
}
|
||||
|
||||
void CanvasContext::destroyCanvasAndSurface() {
|
||||
@@ -403,7 +405,16 @@ void CanvasContext::processLayerUpdates(const Vector<DeferredLayerUpdater*>* lay
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
|
||||
void CanvasContext::prepareTree(TreeInfo& info) {
|
||||
mRootRenderNode->prepareTree(info);
|
||||
|
||||
if (info.hasAnimations && !info.hasFunctors) {
|
||||
// TODO: Functors
|
||||
mRenderThread.postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CanvasContext::draw(Rect* dirty) {
|
||||
LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
|
||||
"drawDisplayList called on a context with no canvas or surface!");
|
||||
|
||||
@@ -417,7 +428,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
|
||||
}
|
||||
|
||||
status_t status;
|
||||
if (dirty) {
|
||||
if (dirty && !dirty->isEmpty()) {
|
||||
status = mCanvas->prepareDirty(dirty->left, dirty->top,
|
||||
dirty->right, dirty->bottom, mOpaque);
|
||||
} else {
|
||||
@@ -425,7 +436,7 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
|
||||
}
|
||||
|
||||
Rect outBounds;
|
||||
status |= mCanvas->drawDisplayList(displayList, outBounds);
|
||||
status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
|
||||
|
||||
// TODO: Draw debug info
|
||||
// TODO: Performance tracking
|
||||
@@ -437,6 +448,20 @@ void CanvasContext::drawDisplayList(RenderNode* displayList, Rect* dirty) {
|
||||
}
|
||||
}
|
||||
|
||||
// Called by choreographer to do an RT-driven animation
|
||||
void CanvasContext::doFrame(nsecs_t frameTimeNanos) {
|
||||
ATRACE_CALL();
|
||||
|
||||
TreeInfo info;
|
||||
info.evaluateAnimations = true;
|
||||
info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNanos);
|
||||
info.performStagingPush = false;
|
||||
info.prepareTextures = false;
|
||||
|
||||
prepareTree(info);
|
||||
draw(NULL);
|
||||
}
|
||||
|
||||
void CanvasContext::invokeFunctor(Functor* functor) {
|
||||
ATRACE_CALL();
|
||||
DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "../RenderNode.h"
|
||||
#include "RenderTask.h"
|
||||
#include "RenderThread.h"
|
||||
|
||||
#define FUNCTOR_PROCESS_DELAY 4
|
||||
|
||||
@@ -39,15 +40,13 @@ class Layer;
|
||||
namespace renderthread {
|
||||
|
||||
class GlobalContext;
|
||||
class CanvasContext;
|
||||
class RenderThread;
|
||||
|
||||
// This per-renderer class manages the bridge between the global EGL context
|
||||
// and the render surface.
|
||||
class CanvasContext {
|
||||
class CanvasContext : public IFrameCallback {
|
||||
public:
|
||||
CanvasContext(bool translucent);
|
||||
~CanvasContext();
|
||||
CanvasContext(bool translucent, RenderNode* rootRenderNode);
|
||||
virtual ~CanvasContext();
|
||||
|
||||
bool initialize(EGLNativeWindowType window);
|
||||
void updateSurface(EGLNativeWindowType window);
|
||||
@@ -55,9 +54,13 @@ public:
|
||||
void setup(int width, int height);
|
||||
void makeCurrent();
|
||||
void processLayerUpdates(const Vector<DeferredLayerUpdater*>* layerUpdaters, TreeInfo& info);
|
||||
void drawDisplayList(RenderNode* displayList, Rect* dirty);
|
||||
void prepareTree(TreeInfo& info);
|
||||
void draw(Rect* dirty);
|
||||
void destroyCanvasAndSurface();
|
||||
|
||||
// IFrameCallback, Chroreographer-driven frame callback entry point
|
||||
virtual void doFrame(nsecs_t frameTimeNanos);
|
||||
|
||||
bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
|
||||
|
||||
void invokeFunctor(Functor* functor);
|
||||
@@ -82,6 +85,8 @@ private:
|
||||
bool mOpaque;
|
||||
OpenGLRenderer* mCanvas;
|
||||
bool mHaveNewSurface;
|
||||
|
||||
const sp<RenderNode> mRootRenderNode;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace android {
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
DrawFrameTask::DrawFrameTask() : mContext(0), mRenderNode(0) {
|
||||
DrawFrameTask::DrawFrameTask() : mContext(0) {
|
||||
}
|
||||
|
||||
DrawFrameTask::~DrawFrameTask() {
|
||||
@@ -55,25 +55,17 @@ void DrawFrameTask::removeLayer(DeferredLayerUpdater* layer) {
|
||||
}
|
||||
}
|
||||
|
||||
void DrawFrameTask::setRenderNode(RenderNode* renderNode) {
|
||||
LOG_ALWAYS_FATAL_IF(!mContext, "Lifecycle violation, there's no context to setRenderNode with!");
|
||||
|
||||
mRenderNode = renderNode;
|
||||
}
|
||||
|
||||
void DrawFrameTask::setDirty(int left, int top, int right, int bottom) {
|
||||
mDirty.set(left, top, right, bottom);
|
||||
}
|
||||
|
||||
void DrawFrameTask::drawFrame(RenderThread* renderThread) {
|
||||
LOG_ALWAYS_FATAL_IF(!mRenderNode.get(), "Cannot drawFrame with no render node!");
|
||||
LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!");
|
||||
|
||||
postAndWait(renderThread);
|
||||
|
||||
// Reset the single-frame data
|
||||
mDirty.setEmpty();
|
||||
mRenderNode = 0;
|
||||
}
|
||||
|
||||
void DrawFrameTask::postAndWait(RenderThread* renderThread) {
|
||||
@@ -88,8 +80,7 @@ void DrawFrameTask::run() {
|
||||
bool canUnblockUiThread = syncFrameState();
|
||||
|
||||
// Grab a copy of everything we need
|
||||
Rect dirtyCopy(mDirty);
|
||||
sp<RenderNode> renderNode = mRenderNode;
|
||||
Rect dirty(mDirty);
|
||||
CanvasContext* context = mContext;
|
||||
|
||||
// From this point on anything in "this" is *UNSAFE TO ACCESS*
|
||||
@@ -97,15 +88,20 @@ void DrawFrameTask::run() {
|
||||
unblockUiThread();
|
||||
}
|
||||
|
||||
drawRenderNode(context, renderNode.get(), &dirtyCopy);
|
||||
context->draw(&dirty);
|
||||
|
||||
if (!canUnblockUiThread) {
|
||||
unblockUiThread();
|
||||
}
|
||||
}
|
||||
|
||||
static void prepareTreeInfo(TreeInfo& info) {
|
||||
static void initTreeInfo(TreeInfo& info) {
|
||||
info.prepareTextures = true;
|
||||
info.performStagingPush = true;
|
||||
info.evaluateAnimations = true;
|
||||
// TODO: Get this from Choreographer
|
||||
nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC);
|
||||
info.frameTimeMs = nanoseconds_to_milliseconds(frameTimeNs);
|
||||
}
|
||||
|
||||
bool DrawFrameTask::syncFrameState() {
|
||||
@@ -113,9 +109,9 @@ bool DrawFrameTask::syncFrameState() {
|
||||
mContext->makeCurrent();
|
||||
Caches::getInstance().textureCache.resetMarkInUse();
|
||||
TreeInfo info;
|
||||
prepareTreeInfo(info);
|
||||
initTreeInfo(info);
|
||||
mContext->processLayerUpdates(&mLayers, info);
|
||||
mRenderNode->prepareTree(info);
|
||||
mContext->prepareTree(info);
|
||||
// If prepareTextures is false, we ran out of texture cache space
|
||||
return !info.hasFunctors && info.prepareTextures;
|
||||
}
|
||||
@@ -125,16 +121,6 @@ void DrawFrameTask::unblockUiThread() {
|
||||
mSignal.signal();
|
||||
}
|
||||
|
||||
void DrawFrameTask::drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty) {
|
||||
ATRACE_CALL();
|
||||
|
||||
if (dirty->bottom == -1 && dirty->left == -1
|
||||
&& dirty->top == -1 && dirty->right == -1) {
|
||||
dirty = 0;
|
||||
}
|
||||
context->drawDisplayList(renderNode, dirty);
|
||||
}
|
||||
|
||||
} /* namespace renderthread */
|
||||
} /* namespace uirenderer */
|
||||
} /* namespace android */
|
||||
|
||||
@@ -53,7 +53,6 @@ public:
|
||||
void addLayer(DeferredLayerUpdater* layer);
|
||||
void removeLayer(DeferredLayerUpdater* layer);
|
||||
|
||||
void setRenderNode(RenderNode* renderNode);
|
||||
void setDirty(int left, int top, int right, int bottom);
|
||||
void drawFrame(RenderThread* renderThread);
|
||||
|
||||
@@ -63,7 +62,6 @@ private:
|
||||
void postAndWait(RenderThread* renderThread);
|
||||
bool syncFrameState();
|
||||
void unblockUiThread();
|
||||
static void drawRenderNode(CanvasContext* context, RenderNode* renderNode, Rect* dirty);
|
||||
|
||||
Mutex mLock;
|
||||
Condition mSignal;
|
||||
@@ -73,7 +71,6 @@ private:
|
||||
/*********************************************
|
||||
* Single frame data
|
||||
*********************************************/
|
||||
sp<RenderNode> mRenderNode;
|
||||
Rect mDirty;
|
||||
|
||||
/*********************************************
|
||||
|
||||
@@ -51,15 +51,16 @@ namespace renderthread {
|
||||
MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
|
||||
ARGS(method) *args = (ARGS(method) *) task->payload()
|
||||
|
||||
CREATE_BRIDGE1(createContext, bool translucent) {
|
||||
return new CanvasContext(args->translucent);
|
||||
CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
|
||||
return new CanvasContext(args->translucent, args->rootRenderNode);
|
||||
}
|
||||
|
||||
RenderProxy::RenderProxy(bool translucent)
|
||||
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
|
||||
: mRenderThread(RenderThread::getInstance())
|
||||
, mContext(0) {
|
||||
SETUP_TASK(createContext);
|
||||
args->translucent = translucent;
|
||||
args->rootRenderNode = rootRenderNode;
|
||||
mContext = (CanvasContext*) postAndWait(task);
|
||||
mDrawFrameTask.setContext(mContext);
|
||||
}
|
||||
@@ -133,9 +134,8 @@ void RenderProxy::setup(int width, int height) {
|
||||
post(task);
|
||||
}
|
||||
|
||||
void RenderProxy::drawDisplayList(RenderNode* displayList,
|
||||
void RenderProxy::syncAndDrawFrame(
|
||||
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) {
|
||||
mDrawFrameTask.setRenderNode(displayList);
|
||||
mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom);
|
||||
mDrawFrameTask.drawFrame(&mRenderThread);
|
||||
}
|
||||
|
||||
@@ -56,14 +56,14 @@ class RenderProxyBridge;
|
||||
*/
|
||||
class ANDROID_API RenderProxy {
|
||||
public:
|
||||
ANDROID_API RenderProxy(bool translucent);
|
||||
ANDROID_API RenderProxy(bool translucent, RenderNode* rootNode);
|
||||
ANDROID_API virtual ~RenderProxy();
|
||||
|
||||
ANDROID_API bool initialize(const sp<ANativeWindow>& window);
|
||||
ANDROID_API void updateSurface(const sp<ANativeWindow>& window);
|
||||
ANDROID_API void pauseSurface(const sp<ANativeWindow>& window);
|
||||
ANDROID_API void setup(int width, int height);
|
||||
ANDROID_API void drawDisplayList(RenderNode* displayList,
|
||||
ANDROID_API void syncAndDrawFrame(
|
||||
int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom);
|
||||
ANDROID_API void destroyCanvasAndSurface();
|
||||
|
||||
|
||||
@@ -18,9 +18,11 @@
|
||||
|
||||
#include "RenderThread.h"
|
||||
|
||||
#include <gui/DisplayEventReceiver.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include "CanvasContext.h"
|
||||
#include "RenderProxy.h"
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace android {
|
||||
using namespace uirenderer::renderthread;
|
||||
@@ -29,6 +31,14 @@ ANDROID_SINGLETON_STATIC_INSTANCE(RenderThread);
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
// Number of events to read at a time from the DisplayEventReceiver pipe.
|
||||
// The value should be large enough that we can quickly drain the pipe
|
||||
// using just a few large reads.
|
||||
static const size_t EVENT_BUFFER_SIZE = 100;
|
||||
|
||||
// Slight delay to give the UI time to push us a new frame before we replay
|
||||
static const int DISPATCH_FRAME_CALLBACKS_DELAY = 0;
|
||||
|
||||
TaskQueue::TaskQueue() : mHead(0), mTail(0) {}
|
||||
|
||||
RenderTask* TaskQueue::next() {
|
||||
@@ -103,8 +113,25 @@ void TaskQueue::remove(RenderTask* task) {
|
||||
}
|
||||
}
|
||||
|
||||
class DispatchFrameCallbacks : public RenderTask {
|
||||
private:
|
||||
RenderThread* mRenderThread;
|
||||
public:
|
||||
DispatchFrameCallbacks(RenderThread* rt) : mRenderThread(rt) {}
|
||||
|
||||
virtual void run() {
|
||||
mRenderThread->dispatchFrameCallbacks();
|
||||
}
|
||||
};
|
||||
|
||||
RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
|
||||
, mNextWakeup(LLONG_MAX) {
|
||||
, mNextWakeup(LLONG_MAX)
|
||||
, mDisplayEventReceiver(0)
|
||||
, mVsyncRequested(false)
|
||||
, mFrameCallbackTaskPending(false)
|
||||
, mFrameCallbackTask(0)
|
||||
, mFrameTime(0) {
|
||||
mFrameCallbackTask = new DispatchFrameCallbacks(this);
|
||||
mLooper = new Looper(false);
|
||||
run("RenderThread");
|
||||
}
|
||||
@@ -112,10 +139,86 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>()
|
||||
RenderThread::~RenderThread() {
|
||||
}
|
||||
|
||||
void RenderThread::initializeDisplayEventReceiver() {
|
||||
LOG_ALWAYS_FATAL_IF(mDisplayEventReceiver, "Initializing a second DisplayEventReceiver?");
|
||||
mDisplayEventReceiver = new DisplayEventReceiver();
|
||||
status_t status = mDisplayEventReceiver->initCheck();
|
||||
LOG_ALWAYS_FATAL_IF(status != NO_ERROR, "Initialization of DisplayEventReceiver "
|
||||
"failed with status: %d", status);
|
||||
|
||||
// Register the FD
|
||||
mLooper->addFd(mDisplayEventReceiver->getFd(), 0,
|
||||
Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
|
||||
}
|
||||
|
||||
int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
|
||||
if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
|
||||
ALOGE("Display event receiver pipe was closed or an error occurred. "
|
||||
"events=0x%x", events);
|
||||
return 0; // remove the callback
|
||||
}
|
||||
|
||||
if (!(events & Looper::EVENT_INPUT)) {
|
||||
ALOGW("Received spurious callback for unhandled poll event. "
|
||||
"events=0x%x", events);
|
||||
return 1; // keep the callback
|
||||
}
|
||||
|
||||
reinterpret_cast<RenderThread*>(data)->drainDisplayEventQueue();
|
||||
|
||||
return 1; // keep the callback
|
||||
}
|
||||
|
||||
static nsecs_t latestVsyncEvent(DisplayEventReceiver* receiver) {
|
||||
DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
|
||||
nsecs_t latest = 0;
|
||||
ssize_t n;
|
||||
while ((n = receiver->getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
|
||||
for (ssize_t i = 0; i < n; i++) {
|
||||
const DisplayEventReceiver::Event& ev = buf[i];
|
||||
switch (ev.header.type) {
|
||||
case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
|
||||
latest = ev.header.timestamp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (n < 0) {
|
||||
ALOGW("Failed to get events from display event receiver, status=%d", status_t(n));
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
|
||||
void RenderThread::drainDisplayEventQueue() {
|
||||
nsecs_t vsyncEvent = latestVsyncEvent(mDisplayEventReceiver);
|
||||
if (vsyncEvent > 0) {
|
||||
mVsyncRequested = false;
|
||||
mFrameTime = vsyncEvent;
|
||||
if (!mFrameCallbackTaskPending) {
|
||||
mFrameCallbackTaskPending = true;
|
||||
//queueDelayed(mFrameCallbackTask, DISPATCH_FRAME_CALLBACKS_DELAY);
|
||||
queue(mFrameCallbackTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RenderThread::dispatchFrameCallbacks() {
|
||||
mFrameCallbackTaskPending = false;
|
||||
|
||||
std::set<IFrameCallback*> callbacks;
|
||||
mFrameCallbacks.swap(callbacks);
|
||||
|
||||
for (std::set<IFrameCallback*>::iterator it = callbacks.begin(); it != callbacks.end(); it++) {
|
||||
(*it)->doFrame(mFrameTime);
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderThread::threadLoop() {
|
||||
initializeDisplayEventReceiver();
|
||||
|
||||
int timeoutMillis = -1;
|
||||
for (;;) {
|
||||
int result = mLooper->pollAll(timeoutMillis);
|
||||
int result = mLooper->pollOnce(timeoutMillis);
|
||||
LOG_ALWAYS_FATAL_IF(result == Looper::POLL_ERROR,
|
||||
"RenderThread Looper POLL_ERROR!");
|
||||
|
||||
@@ -159,6 +262,20 @@ void RenderThread::remove(RenderTask* task) {
|
||||
mQueue.remove(task);
|
||||
}
|
||||
|
||||
void RenderThread::postFrameCallback(IFrameCallback* callback) {
|
||||
mFrameCallbacks.insert(callback);
|
||||
if (!mVsyncRequested) {
|
||||
mVsyncRequested = true;
|
||||
status_t status = mDisplayEventReceiver->requestNextVsync();
|
||||
LOG_ALWAYS_FATAL_IF(status != NO_ERROR,
|
||||
"requestNextVsync failed with status: %d", status);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderThread::removeFrameCallback(IFrameCallback* callback) {
|
||||
mFrameCallbacks.erase(callback);
|
||||
}
|
||||
|
||||
RenderTask* RenderThread::nextTask(nsecs_t* nextWakeup) {
|
||||
AutoMutex _lock(mLock);
|
||||
RenderTask* next = mQueue.peek();
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
#define RENDERTHREAD_H_
|
||||
|
||||
#include "RenderTask.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <utils/Looper.h>
|
||||
#include <utils/Mutex.h>
|
||||
@@ -25,9 +29,13 @@
|
||||
#include <utils/Thread.h>
|
||||
|
||||
namespace android {
|
||||
class DisplayEventReceiver;
|
||||
|
||||
namespace uirenderer {
|
||||
namespace renderthread {
|
||||
|
||||
class DispatchFrameCallbacks;
|
||||
|
||||
class TaskQueue {
|
||||
public:
|
||||
TaskQueue();
|
||||
@@ -42,6 +50,15 @@ private:
|
||||
RenderTask* mTail;
|
||||
};
|
||||
|
||||
// Mimics android.view.Choreographer.FrameCallback
|
||||
class IFrameCallback {
|
||||
public:
|
||||
virtual void doFrame(nsecs_t frameTimeNanos) = 0;
|
||||
|
||||
protected:
|
||||
~IFrameCallback() {}
|
||||
};
|
||||
|
||||
class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
|
||||
public:
|
||||
// RenderThread takes complete ownership of tasks that are queued
|
||||
@@ -50,15 +67,25 @@ public:
|
||||
void queueDelayed(RenderTask* task, int delayMs);
|
||||
void remove(RenderTask* task);
|
||||
|
||||
// Mimics android.view.Choreographer
|
||||
void postFrameCallback(IFrameCallback* callback);
|
||||
void removeFrameCallback(IFrameCallback* callback);
|
||||
|
||||
protected:
|
||||
virtual bool threadLoop();
|
||||
|
||||
private:
|
||||
friend class Singleton<RenderThread>;
|
||||
friend class DispatchFrameCallbacks;
|
||||
|
||||
RenderThread();
|
||||
virtual ~RenderThread();
|
||||
|
||||
void initializeDisplayEventReceiver();
|
||||
static int displayEventReceiverCallback(int fd, int events, void* data);
|
||||
void drainDisplayEventQueue();
|
||||
void dispatchFrameCallbacks();
|
||||
|
||||
// Returns the next task to be run. If this returns NULL nextWakeup is set
|
||||
// to the time to requery for the nextTask to run. mNextWakeup is also
|
||||
// set to this time
|
||||
@@ -69,6 +96,13 @@ private:
|
||||
|
||||
nsecs_t mNextWakeup;
|
||||
TaskQueue mQueue;
|
||||
|
||||
DisplayEventReceiver* mDisplayEventReceiver;
|
||||
bool mVsyncRequested;
|
||||
std::set<IFrameCallback*> mFrameCallbacks;
|
||||
bool mFrameCallbackTaskPending;
|
||||
DispatchFrameCallbacks* mFrameCallbackTask;
|
||||
nsecs_t mFrameTime;
|
||||
};
|
||||
|
||||
} /* namespace renderthread */
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
|
||||
package com.example.renderthread;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.view.RenderNode;
|
||||
import android.view.HardwareRenderer;
|
||||
import android.view.ThreadedRenderer;
|
||||
import android.view.RenderNodeAnimator;
|
||||
import android.view.View;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListView;
|
||||
@@ -23,6 +19,8 @@ import java.util.Map;
|
||||
|
||||
public class MainActivity extends Activity implements OnItemClickListener {
|
||||
|
||||
static final int TRANSLATION_Y = 1;
|
||||
static final int DELTA_TYPE_DELTA = 1;
|
||||
static final int DURATION = 400;
|
||||
|
||||
static final String KEY_NAME = "name";
|
||||
@@ -66,82 +64,21 @@ public class MainActivity extends Activity implements OnItemClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
private static class DisplayListAnimator {
|
||||
private static final TimeInterpolator sDefaultInterpolator =
|
||||
new AccelerateDecelerateInterpolator();
|
||||
|
||||
RenderNode mDisplayList;
|
||||
float mFromValue;
|
||||
float mDelta;
|
||||
long mDuration = DURATION * 2;
|
||||
long mStartTime;
|
||||
|
||||
DisplayListAnimator(View view, float translateXBy) {
|
||||
mDelta = translateXBy;
|
||||
mFromValue = view.getTranslationY();
|
||||
mDisplayList = view.getDisplayList();
|
||||
}
|
||||
|
||||
boolean animate(long currentTime) {
|
||||
if (mStartTime == 0) mStartTime = currentTime;
|
||||
|
||||
float fraction = (float)(currentTime - mStartTime) / mDuration;
|
||||
if (fraction > 1) {
|
||||
return false;
|
||||
}
|
||||
fraction = sDefaultInterpolator.getInterpolation(fraction);
|
||||
float translation = mFromValue + (mDelta * fraction);
|
||||
mDisplayList.setTranslationY(translation);
|
||||
return fraction < 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private static class AnimationExecutor implements Runnable {
|
||||
DisplayListAnimator[] mAnimations;
|
||||
ThreadedRenderer mRenderer;
|
||||
|
||||
AnimationExecutor(ThreadedRenderer renderer, DisplayListAnimator[] animations) {
|
||||
mRenderer = renderer;
|
||||
mAnimations = animations;
|
||||
ThreadedRenderer.postToRenderThread(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
boolean hasMore = false;
|
||||
long now = SystemClock.uptimeMillis();
|
||||
for (DisplayListAnimator animator : mAnimations) {
|
||||
hasMore |= animator.animate(now);
|
||||
}
|
||||
mRenderer.repeatLastDraw();
|
||||
if (hasMore) {
|
||||
ThreadedRenderer.postToRenderThread(this);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(final AdapterView<?> adapterView, View clickedView,
|
||||
int clickedPosition, long clickedId) {
|
||||
int topPosition = adapterView.getFirstVisiblePosition();
|
||||
int dy = adapterView.getHeight();
|
||||
final DisplayListAnimator[] animators = new DisplayListAnimator[adapterView.getChildCount()];
|
||||
for (int i = 0; i < adapterView.getChildCount(); i++) {
|
||||
int pos = topPosition + i;
|
||||
View child = adapterView.getChildAt(i);
|
||||
float delta = (pos - clickedPosition) * 1.1f;
|
||||
if (delta == 0) delta = -1;
|
||||
animators[i] = new DisplayListAnimator(child, dy * delta);
|
||||
RenderNodeAnimator animator = new RenderNodeAnimator(
|
||||
TRANSLATION_Y, DELTA_TYPE_DELTA, dy * delta);
|
||||
animator.setDuration(DURATION);
|
||||
animator.start(child);
|
||||
}
|
||||
adapterView.invalidate();
|
||||
adapterView.post(new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
new AnimationExecutor((ThreadedRenderer) adapterView.getHardwareRenderer(), animators);
|
||||
}
|
||||
});
|
||||
//mHandler.postDelayed(mLaunchActivity, (long) (DURATION * .4));
|
||||
mLaunchActivity.run();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user