diff options
author | John Reck <jreck@google.com> | 2014-06-03 15:53:15 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2014-06-04 12:21:26 -0700 |
commit | e4267ea4f20740c37c01bfb6aefcf61fddc4566a (patch) | |
tree | 0d00c8bff43f8b0cbe4284e51299f5ca128c83c3 | |
parent | 79c7de77a7da9cbcb9428ab6203987feb35a427f (diff) | |
download | frameworks_base-e4267ea4f20740c37c01bfb6aefcf61fddc4566a.zip frameworks_base-e4267ea4f20740c37c01bfb6aefcf61fddc4566a.tar.gz frameworks_base-e4267ea4f20740c37c01bfb6aefcf61fddc4566a.tar.bz2 |
Even FASTER damage calculations!
* Now with more native!
* Less matrix math thanks to bulk-property-update support!
* Zero JNI on the View.damageInParent() path!
* Fully aware of RT-driven animators!
* Likely full of new and exciting bugs!
* But it also fixes at least 1 existing invalidate bug!
Change-Id: Ie0773f85a60850ff2668370c58defef2e8aa079f
27 files changed, 355 insertions, 85 deletions
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java index 6acb134..b5b9199 100644 --- a/core/java/android/view/HardwareLayer.java +++ b/core/java/android/view/HardwareLayer.java @@ -217,8 +217,6 @@ final class HardwareLayer { private static native void nUpdateRenderLayer(long layerUpdater, long displayList, int left, int top, int right, int bottom); - private static native boolean nFlushChanges(long layerUpdater); - private static native long nGetLayer(long layerUpdater); private static native int nGetTexName(long layerUpdater); } diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index d67c974..592dec8 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -363,8 +363,7 @@ public abstract class HardwareRenderer { * @param callbacks Callbacks invoked when drawing happens. * @param dirty The dirty rectangle to update, can be null. */ - abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks, - Rect dirty); + abstract void draw(View view, View.AttachInfo attachInfo, HardwareDrawCallbacks callbacks); /** * Creates a new hardware layer. A hardware layer built by calling this diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index c165475..4631b64 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -842,6 +842,13 @@ public class RenderNode { } /** + * Sets the scroll position, this is used for damage calculations + */ + public void setScrollPosition(int x, int y) { + nSetScrollPosition(mNativeRenderNode, x, y); + } + + /** * Outputs the display list to the log. This method exists for use by * tools to output display lists for selected nodes to the log. * @@ -899,6 +906,7 @@ public class RenderNode { private static native boolean nSetRight(long renderNode, int right); private static native boolean nSetTop(long renderNode, int top); private static native boolean nSetLeft(long renderNode, int left); + private static native void nSetScrollPosition(long renderNode, int scrollX, int scrollY); private static native boolean nSetCameraDistance(long renderNode, float distance); private static native boolean nSetPivotY(long renderNode, float pivotY); private static native boolean nSetPivotX(long renderNode, float pivotX); diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 9b3ef7f..7bbe84e 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -54,8 +54,6 @@ import java.io.PrintWriter; public class ThreadedRenderer extends HardwareRenderer { private static final String LOGTAG = "ThreadedRenderer"; - private static final Rect NULL_RECT = new Rect(); - // Keep in sync with DrawFrameTask.h SYNC_* flags // Nothing interesting to report private static final int SYNC_OK = 0x0; @@ -228,7 +226,7 @@ public class ThreadedRenderer extends HardwareRenderer { } @Override - void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks, Rect dirty) { + void draw(View view, AttachInfo attachInfo, HardwareDrawCallbacks callbacks) { attachInfo.mIgnoreDirtyState = true; long frameTimeNanos = mChoreographer.getFrameTimeNanos(); attachInfo.mDrawingTime = frameTimeNanos / TimeUtils.NANOS_PER_MS; @@ -246,12 +244,8 @@ public class ThreadedRenderer extends HardwareRenderer { attachInfo.mIgnoreDirtyState = false; - if (dirty == null) { - dirty = NULL_RECT; - } int syncResult = nSyncAndDrawFrame(mNativeProxy, frameTimeNanos, - recordDuration, view.getResources().getDisplayMetrics().density, - dirty.left, dirty.top, dirty.right, dirty.bottom); + recordDuration, view.getResources().getDisplayMetrics().density); if ((syncResult & SYNC_INVALIDATE_REQUIRED) != 0) { attachInfo.mViewRootImpl.invalidate(); } @@ -393,8 +387,7 @@ public class ThreadedRenderer extends HardwareRenderer { float lightX, float lightY, float lightZ, float lightRadius); private static native void nSetOpaque(long nativeProxy, boolean opaque); private static native int nSyncAndDrawFrame(long nativeProxy, - long frameTimeNanos, long recordDuration, float density, - int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + long frameTimeNanos, long recordDuration, float density); private static native void nRunWithGlContext(long nativeProxy, Runnable runnable); private static native void nDestroyCanvasAndSurface(long nativeProxy); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b500e46..6cac755 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13750,6 +13750,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return; } + renderNode.setScrollPosition(mScrollX, mScrollY); if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 || !renderNode.isValid() || (!isLayer && mRecreateDisplayList)) { diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 0f40ee7..02011e0 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4530,6 +4530,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Native-calculated damage path + * Returns false if this path was unable to complete successfully. This means + * it hit a ViewParent it doesn't recognize and needs to fall back to calculating + * damage area + * @hide + */ + public boolean damageChildDeferred(View child) { + ViewParent parent = getParent(); + while (parent != null) { + if (parent instanceof ViewGroup) { + parent = parent.getParent(); + } else if (parent instanceof ViewRootImpl) { + ((ViewRootImpl) parent).invalidate(); + return true; + } else { + parent = null; + } + } + return false; + } + + /** * Quick invalidation method called by View.invalidateViewProperty. This doesn't set the * DRAWN flags and doesn't handle the Animation logic that the default invalidation methods * do; all we want to do here is schedule a traversal with the appropriate dirty rect. @@ -4537,6 +4559,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * @hide */ public void damageChild(View child, final Rect dirty) { + if (damageChildDeferred(child)) { + return; + } + ViewParent parent = this; final AttachInfo attachInfo = mAttachInfo; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f3d1e3c..76d5038 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -181,7 +181,6 @@ public final class ViewRootImpl implements ViewParent, int mWidth; int mHeight; Rect mDirty; - final Rect mCurrentDirty = new Rect(); boolean mIsAnimating; CompatibilityInfo.Translator mTranslator; @@ -861,7 +860,9 @@ public final class ViewRootImpl implements ViewParent, void invalidate() { mDirty.set(0, 0, mWidth, mHeight); - scheduleTraversals(); + if (!mWillDrawSoon) { + scheduleTraversals(); + } } void invalidateWorld(View view) { @@ -2436,12 +2437,10 @@ public final class ViewRootImpl implements ViewParent, mHardwareYOffset = yoff; mResizeAlpha = resizeAlpha; - mCurrentDirty.set(dirty); dirty.setEmpty(); mBlockResizeBuffer = false; - attachInfo.mHardwareRenderer.draw(mView, attachInfo, this, - animating ? null : mCurrentDirty); + attachInfo.mHardwareRenderer.draw(mView, attachInfo, this); } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface diff --git a/core/jni/android_view_HardwareLayer.cpp b/core/jni/android_view_HardwareLayer.cpp index 879836d..64b077b 100644 --- a/core/jni/android_view_HardwareLayer.cpp +++ b/core/jni/android_view_HardwareLayer.cpp @@ -95,13 +95,6 @@ static void android_view_HardwareLayer_updateRenderLayer(JNIEnv* env, jobject cl layer->setDisplayList(displayList, left, top, right, bottom); } -static jboolean android_view_HardwareLayer_flushChanges(JNIEnv* env, jobject clazz, - jlong layerUpdaterPtr) { - DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); - TreeInfo ignoredInfo; - return layer->apply(ignoredInfo); -} - static jlong android_view_HardwareLayer_getLayer(JNIEnv* env, jobject clazz, jlong layerUpdaterPtr) { DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerUpdaterPtr); @@ -135,8 +128,6 @@ static JNINativeMethod gMethods[] = { { "nUpdateSurfaceTexture", "(J)V", (void*) android_view_HardwareLayer_updateSurfaceTexture }, { "nUpdateRenderLayer", "(JJIIII)V", (void*) android_view_HardwareLayer_updateRenderLayer }, - { "nFlushChanges", "(J)Z", (void*) android_view_HardwareLayer_flushChanges }, - { "nGetLayer", "(J)J", (void*) android_view_HardwareLayer_getLayer }, { "nGetTexName", "(J)I", (void*) android_view_HardwareLayer_getTexName }, #endif diff --git a/core/jni/android_view_RenderNode.cpp b/core/jni/android_view_RenderNode.cpp index 26f8993..e5f79f2 100644 --- a/core/jni/android_view_RenderNode.cpp +++ b/core/jni/android_view_RenderNode.cpp @@ -275,6 +275,13 @@ static jboolean android_view_RenderNode_offsetTopAndBottom(JNIEnv* env, return SET_AND_DIRTY(offsetTopBottom, offset, RenderNode::Y); } +static void android_view_RenderNode_setScrollPosition(JNIEnv* env, + jobject clazz, jlong renderNodePtr, jint scrollX, jint scrollY) { + RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); + SET_AND_DIRTY(setScrollX, scrollX, RenderNode::GENERIC); + SET_AND_DIRTY(setScrollY, scrollY, RenderNode::GENERIC); +} + // ---------------------------------------------------------------------------- // RenderProperties - getters // ---------------------------------------------------------------------------- @@ -510,6 +517,7 @@ static JNINativeMethod gMethods[] = { { "nSetLeftTopRightBottom","(JIIII)Z", (void*) android_view_RenderNode_setLeftTopRightBottom }, { "nOffsetLeftAndRight", "(JF)Z", (void*) android_view_RenderNode_offsetLeftAndRight }, { "nOffsetTopAndBottom", "(JF)Z", (void*) android_view_RenderNode_offsetTopAndBottom }, + { "nSetScrollPosition", "(JII)V", (void*) android_view_RenderNode_setScrollPosition }, { "nHasOverlappingRendering", "(J)Z", (void*) android_view_RenderNode_hasOverlappingRendering }, { "nGetClipToOutline", "(J)Z", (void*) android_view_RenderNode_getClipToOutline }, diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index a550649..815c4a7 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -150,6 +150,13 @@ public: } } +protected: + virtual void damageSelf(TreeInfo& info) { + // Intentionally a no-op. As RootRenderNode gets a new DisplayListData + // every frame this would result in every draw push being a full inval, + // which is wrong. Only RootRenderNode has this issue. + } + private: sp<Looper> mLooper; std::vector<OnFinishedEvent> mOnFinishedEvents; @@ -242,11 +249,9 @@ static void android_view_ThreadedRenderer_setOpaque(JNIEnv* env, jobject clazz, } static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz, - jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density, - jint dirtyLeft, jint dirtyTop, jint dirtyRight, jint dirtyBottom) { + jlong proxyPtr, jlong frameTimeNanos, jlong recordDuration, jfloat density) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); - return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density, - dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + return proxy->syncAndDrawFrame(frameTimeNanos, recordDuration, density); } static void android_view_ThreadedRenderer_destroyCanvasAndSurface(JNIEnv* env, jobject clazz, @@ -363,7 +368,7 @@ static JNINativeMethod gMethods[] = { { "nPauseSurface", "(JLandroid/view/Surface;)V", (void*) android_view_ThreadedRenderer_pauseSurface }, { "nSetup", "(JIIFFFF)V", (void*) android_view_ThreadedRenderer_setup }, { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque }, - { "nSyncAndDrawFrame", "(JJJFIIII)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame }, + { "nSyncAndDrawFrame", "(JJJF)I", (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 }, diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index e5c8898..cc62170 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -14,6 +14,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) AmbientShadow.cpp \ Animator.cpp \ AssetAtlas.cpp \ + DamageAccumulator.cpp \ FontRenderer.cpp \ GammaFontRenderer.cpp \ Caches.cpp \ diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp new file mode 100644 index 0000000..8aa8c92 --- /dev/null +++ b/libs/hwui/DamageAccumulator.cpp @@ -0,0 +1,105 @@ +/* + * 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 "DamageAccumulator" + +#include "DamageAccumulator.h" + +#include <cutils/log.h> + +#include "RenderNode.h" +#include "utils/MathUtils.h" + +namespace android { +namespace uirenderer { + +struct DirtyStack { + const RenderNode* node; + // When this frame is pop'd, this rect is mapped through the above transform + // and applied to the previous (aka parent) frame + SkRect pendingDirty; + DirtyStack* prev; + DirtyStack* next; +}; + +DamageAccumulator::DamageAccumulator() { + mHead = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); + memset(mHead, 0, sizeof(DirtyStack)); + // Create a root that we will not pop off + mHead->prev = mHead; +} + +void DamageAccumulator::pushNode(const RenderNode* node) { + if (!mHead->next) { + DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); + nextFrame->next = 0; + nextFrame->prev = mHead; + mHead->next = nextFrame; + } + mHead = mHead->next; + mHead->node = node; + mHead->pendingDirty.setEmpty(); +} + +void DamageAccumulator::popNode() { + LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!"); + DirtyStack* dirtyFrame = mHead; + mHead = mHead->prev; + if (!dirtyFrame->pendingDirty.isEmpty()) { + SkRect mappedDirty; + const RenderProperties& props = dirtyFrame->node->properties(); + const SkMatrix* transform = props.getTransformMatrix(); + if (transform && !transform->isIdentity()) { + transform->mapRect(&mappedDirty, dirtyFrame->pendingDirty); + } else { + mappedDirty = dirtyFrame->pendingDirty; + } + if (CC_LIKELY(mHead->node)) { + const RenderProperties& parentProps = mHead->node->properties(); + mappedDirty.offset(props.getLeft() - parentProps.getScrollX(), + props.getTop() - parentProps.getScrollY()); + if (props.getClipToBounds()) { + if (!mappedDirty.intersect(0, 0, parentProps.getWidth(), parentProps.getHeight())) { + mappedDirty.setEmpty(); + } + } + if (CC_UNLIKELY(!MathUtils::isZero(props.getTranslationZ()))) { + // TODO: Can we better bound the shadow damage area? For now + // match the old damageShadowReceiver() path and just dirty + // the entire parent bounds + mappedDirty.join(0, 0, parentProps.getWidth(), parentProps.getHeight()); + } + } else { + mappedDirty.offset(props.getLeft(), props.getTop()); + } + dirty(mappedDirty.fLeft, mappedDirty.fTop, mappedDirty.fRight, mappedDirty.fBottom); + } +} + +void DamageAccumulator::dirty(float left, float top, float right, float bottom) { + mHead->pendingDirty.join(left, top, right, bottom); +} + +void DamageAccumulator::finish(SkRect* totalDirty) { + LOG_ALWAYS_FATAL_IF(mHead->prev != mHead, "Cannot finish, mismatched push/pop calls! %p vs. %p", mHead->prev, mHead); + // Root node never has a transform, so this is the fully mapped dirty rect + *totalDirty = mHead->pendingDirty; + totalDirty->roundOut(); + mHead->pendingDirty.setEmpty(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h new file mode 100644 index 0000000..c62a351 --- /dev/null +++ b/libs/hwui/DamageAccumulator.h @@ -0,0 +1,57 @@ +/* + * 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 DAMAGEACCUMULATOR_H +#define DAMAGEACCUMULATOR_H + +#include <utils/LinearAllocator.h> + +#include <SkMatrix.h> +#include <SkRect.h> + +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +struct DirtyStack; +class RenderNode; + +class DamageAccumulator { + PREVENT_COPY_AND_ASSIGN(DamageAccumulator); +public: + DamageAccumulator(); + // mAllocator will clean everything up for us, no need for a dtor + + // Push a transform node onto the stack. This should be called prior + // to any dirty() calls. Subsequent calls to dirty() + // will be affected by the node's transform when popNode() is called. + void pushNode(const RenderNode* node); + // Pops a transform node from the stack, propagating the dirty rect + // up to the parent node. + void popNode(); + void dirty(float left, float top, float right, float bottom); + + void finish(SkRect* totalDirty); + +private: + LinearAllocator mAllocator; + DirtyStack* mHead; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DAMAGEACCUMULATOR_H */ diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 97e9bf6..d494c4c 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -81,6 +81,10 @@ bool DeferredLayerUpdater::apply(TreeInfo& info) { success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight); } mLayer->setBlend(mBlend); + // TODO: Use DamageAccumulator to get the damage area for the layer's + // subtree to only update that part of the layer. Do this as part of + // reworking layers to be a RenderProperty instead of a View-managed object + mDirtyRect.set(0, 0, mWidth, mHeight); mDisplayList->prepareTree(info); mLayer->updateDeferred(mDisplayList.get(), mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom); diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp index 971a66e..2409554 100644 --- a/libs/hwui/DrawProfiler.cpp +++ b/libs/hwui/DrawProfiler.cpp @@ -109,7 +109,7 @@ void DrawProfiler::finishFrame() { mCurrentFrame = (mCurrentFrame + 1) % mDataSize; } -void DrawProfiler::unionDirty(Rect* dirty) { +void DrawProfiler::unionDirty(SkRect* dirty) { RETURN_IF_DISABLED(); // Not worth worrying about minimizing the dirty region for debugging, so just // dirty the entire viewport. diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h index c1aa1c6..7c06e5d 100644 --- a/libs/hwui/DrawProfiler.h +++ b/libs/hwui/DrawProfiler.h @@ -37,7 +37,7 @@ public: void markPlaybackEnd(); void finishFrame(); - void unionDirty(Rect* dirty); + void unionDirty(SkRect* dirty); void draw(OpenGLRenderer* canvas); void dumpData(int fd); diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index baf372a..c2f6df8 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -25,6 +25,7 @@ #include <utils/Trace.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "DisplayListOp.h" #include "DisplayListLogBuffer.h" @@ -110,14 +111,36 @@ void RenderNode::prepareTree(TreeInfo& info) { prepareTreeImpl(info); } +static inline void pushNode(RenderNode* self, TreeInfo& info) { + if (info.damageAccumulator) { + info.damageAccumulator->pushNode(self); + } +} + +static inline void popNode(TreeInfo& info) { + if (info.damageAccumulator) { + info.damageAccumulator->popNode(); + } +} + +void RenderNode::damageSelf(TreeInfo& info) { + if (info.damageAccumulator && isRenderable() && properties().getAlpha() > 0) { + info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + } +} + void RenderNode::prepareTreeImpl(TreeInfo& info) { - if (info.performStagingPush) { + pushNode(this, info); + if (info.mode == TreeInfo::MODE_FULL) { pushStagingChanges(info); - } - if (info.evaluateAnimations) { + evaluateAnimations(info); + } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) { + pushStagingChanges(info); + } else if (info.mode == TreeInfo::MODE_RT_ONLY) { evaluateAnimations(info); } prepareSubTree(info, mDisplayListData); + popNode(info); } class PushAnimatorsFunctor { @@ -149,18 +172,28 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { } if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; + damageSelf(info); + popNode(info); mProperties = mStagingProperties; + pushNode(this, info); + // We could try to be clever and only re-damage if the matrix changed. + // However, we don't need to worry about that. The cost of over-damaging + // here is only going to be a single additional map rect of this node + // plus a rect join(). The parent's transform (and up) will only be + // performed once. + damageSelf(info); } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData // that are no longer used - TreeInfo oldTreeInfo; + TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING); + oldTreeInfo.damageAccumulator = info.damageAccumulator; prepareSubTree(oldTreeInfo, mDisplayListData); - // TODO: The damage for the old tree should be accounted for delete mDisplayListData; mDisplayListData = mStagingDisplayListData; mStagingDisplayListData = 0; + damageSelf(info); } } @@ -180,12 +213,21 @@ private: void RenderNode::evaluateAnimations(TreeInfo& info) { if (!mAnimators.size()) return; + // TODO: Can we target this better? For now treat it like any other staging + // property push and just damage self before and after animators are run + + damageSelf(info); + popNode(info); + AnimateFunctor functor(this, info); std::vector< sp<BaseRenderNodeAnimator> >::iterator newEnd; newEnd = std::remove_if(mAnimators.begin(), mAnimators.end(), functor); mAnimators.erase(newEnd, mAnimators.end()); mProperties.updateMatrix(); info.out.hasAnimations |= mAnimators.size(); + + pushNode(this, info); + damageSelf(info); } void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1a5377b..393d4ea 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -148,7 +148,7 @@ public: mDirtyPropertyFields |= fields; } - const RenderProperties& properties() { + const RenderProperties& properties() const { return mProperties; } @@ -187,6 +187,9 @@ public: mNeedsAnimatorsSync = true; } +protected: + virtual void damageSelf(TreeInfo& info); + private: typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 5f7d4e3..6163df5 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -44,6 +44,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields() , mPivotX(0), mPivotY(0) , mLeft(0), mTop(0), mRight(0), mBottom(0) , mWidth(0), mHeight(0) + , mScrollX(0), mScrollY(0) , mPivotExplicitlySet(false) , mMatrixOrPivotDirty(false) , mCaching(false) { diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index b012fc5..c294f38 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -366,6 +366,22 @@ public: return false; } + bool setScrollX(int scrollX) { + return RP_SET(mPrimitiveFields.mScrollX, scrollX); + } + + bool setScrollY(int scrollY) { + return RP_SET(mPrimitiveFields.mScrollY, scrollY); + } + + int getScrollX() const { + return mPrimitiveFields.mScrollX; + } + + int getScrollY() const { + return mPrimitiveFields.mScrollY; + } + bool setCaching(bool caching) { return RP_SET(mPrimitiveFields.mCaching, caching); } @@ -465,6 +481,7 @@ private: float mPivotX, mPivotY; int mLeft, mTop, mRight, mBottom; int mWidth, mHeight; + int mScrollX, mScrollY; bool mPivotExplicitlySet; bool mMatrixOrPivotDirty; bool mCaching; diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 8355f83..2096f98 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -18,11 +18,14 @@ #include <utils/Timers.h> +#include "utils/Macros.h" + namespace android { namespace uirenderer { class BaseRenderNodeAnimator; class AnimationListener; +class DamageAccumulator; class AnimationHook { public: @@ -31,21 +34,44 @@ protected: ~AnimationHook() {} }; -struct TreeInfo { - // The defaults here should be safe for everyone but DrawFrameTask to use as-is. - TreeInfo() - : frameTimeMs(0) +// This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN +class TreeInfo { + PREVENT_COPY_AND_ASSIGN(TreeInfo); +public: + enum TraversalMode { + // The full monty - sync, push, run animators, etc... Used by DrawFrameTask + // May only be used if both the UI thread and RT thread are blocked on the + // prepare + MODE_FULL, + // Run only what can be done safely on RT thread. Currently this only means + // animators, but potentially things like SurfaceTexture updates + // could be handled by this as well if there are no listeners + MODE_RT_ONLY, + // The subtree is being detached. Maybe. If the RenderNode is present + // in both the old and new display list's children then it will get a + // MODE_MAYBE_DETACHING followed shortly by a MODE_FULL. + // Push any pending display list changes in case it is detached, + // but don't evaluate animators and such as if it isn't detached as a + // MODE_FULL will follow shortly. + MODE_MAYBE_DETACHING, + // TODO: TRIM_MEMORY? + }; + + explicit TreeInfo(TraversalMode mode) + : mode(mode) + , frameTimeMs(0) , animationHook(NULL) - , prepareTextures(false) - , performStagingPush(true) - , evaluateAnimations(false) + , prepareTextures(mode == MODE_FULL) + , damageAccumulator(0) {} + const TraversalMode mode; nsecs_t frameTimeMs; AnimationHook* animationHook; + // TODO: Remove this? Currently this is used to signal to stop preparing + // textures if we run out of cache space. bool prepareTextures; - bool performStagingPush; - bool evaluateAnimations; + DamageAccumulator* damageAccumulator; struct Out { Out() diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 9ebee1d..8a5c857 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -439,6 +439,7 @@ void CanvasContext::prepareTree(TreeInfo& info) { mRenderThread.removeFrameCallback(this); info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); + info.damageAccumulator = &mDamageAccumulator; mRootRenderNode->prepareTree(info); int runningBehind = 0; @@ -465,27 +466,30 @@ void CanvasContext::notifyFramePending() { mRenderThread.pushBackFrameCallback(this); } -void CanvasContext::draw(Rect* dirty) { +void CanvasContext::draw() { LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE, "drawDisplayList called on a context with no canvas or surface!"); profiler().markPlaybackStart(); + SkRect dirty; + mDamageAccumulator.finish(&dirty); + EGLint width, height; mGlobalContext->beginFrame(mEglSurface, &width, &height); if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); - dirty = NULL; + dirty.setEmpty(); } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { - dirty = NULL; + dirty.setEmpty(); } else { - profiler().unionDirty(dirty); + profiler().unionDirty(&dirty); } status_t status; - if (dirty && !dirty->isEmpty()) { - status = mCanvas->prepareDirty(dirty->left, dirty->top, - dirty->right, dirty->bottom, mOpaque); + if (!dirty.isEmpty()) { + status = mCanvas->prepareDirty(dirty.fLeft, dirty.fTop, + dirty.fRight, dirty.fBottom, mOpaque); } else { status = mCanvas->prepare(mOpaque); } @@ -516,14 +520,12 @@ void CanvasContext::doFrame() { profiler().startFrame(); - TreeInfo info; - info.evaluateAnimations = true; - info.performStagingPush = false; + TreeInfo info(TreeInfo::MODE_RT_ONLY); info.prepareTextures = false; prepareTree(info); if (info.out.canDrawThisFrame) { - draw(NULL); + draw(); } } @@ -543,7 +545,7 @@ void CanvasContext::invokeFunctor(Functor* functor) { bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { requireGlContext(); - TreeInfo info; + TreeInfo info(TreeInfo::MODE_FULL); layer->apply(info); return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 00c5bf0..d926b38 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -23,6 +23,7 @@ #include <utils/Functor.h> #include <utils/Vector.h> +#include "../DamageAccumulator.h" #include "../DrawProfiler.h" #include "../RenderNode.h" #include "RenderTask.h" @@ -57,7 +58,7 @@ public: void makeCurrent(); void processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info); void prepareTree(TreeInfo& info); - void draw(Rect* dirty); + void draw(); void destroyCanvasAndSurface(); // IFrameCallback, Chroreographer-driven frame callback entry point @@ -99,6 +100,7 @@ private: bool mOpaque; OpenGLRenderer* mCanvas; bool mHaveNewSurface; + DamageAccumulator mDamageAccumulator; const sp<RenderNode> mRootRenderNode; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 61d67ca..bdfdd21 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -68,10 +68,6 @@ void DrawFrameTask::removeLayerUpdate(DeferredLayerUpdater* layer) { } } -void DrawFrameTask::setDirty(int left, int top, int right, int bottom) { - mDirty.set(left, top, right, bottom); -} - int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos) { LOG_ALWAYS_FATAL_IF(!mContext, "Cannot drawFrame with no CanvasContext!"); @@ -83,7 +79,6 @@ int DrawFrameTask::drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos // Reset the single-frame data mFrameTimeNanos = 0; mRecordDurationNanos = 0; - mDirty.setEmpty(); return mSyncResult; } @@ -103,13 +98,12 @@ void DrawFrameTask::run() { bool canUnblockUiThread; bool canDrawThisFrame; { - TreeInfo info; + TreeInfo info(TreeInfo::MODE_FULL); canUnblockUiThread = syncFrameState(info); canDrawThisFrame = info.out.canDrawThisFrame; } // Grab a copy of everything we need - Rect dirty(mDirty); CanvasContext* context = mContext; // From this point on anything in "this" is *UNSAFE TO ACCESS* @@ -118,7 +112,7 @@ void DrawFrameTask::run() { } if (CC_LIKELY(canDrawThisFrame)) { - context->draw(&dirty); + context->draw(); } if (!canUnblockUiThread) { @@ -126,18 +120,11 @@ void DrawFrameTask::run() { } } -static void initTreeInfo(TreeInfo& info) { - info.prepareTextures = true; - info.performStagingPush = true; - info.evaluateAnimations = true; -} - bool DrawFrameTask::syncFrameState(TreeInfo& info) { ATRACE_CALL(); mRenderThread->timeLord().vsyncReceived(mFrameTimeNanos); mContext->makeCurrent(); Caches::getInstance().textureCache.resetMarkInUse(); - initTreeInfo(info); for (size_t i = 0; i < mLayers.size(); i++) { mContext->processLayerUpdate(mLayers[i].get(), info); @@ -149,8 +136,6 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mContext->prepareTree(info); if (info.out.hasAnimations) { - // TODO: dirty calculations, for now just do a full-screen inval - mDirty.setEmpty(); if (info.out.requiresUiRedraw) { mSyncResult |= kSync_UIRedrawRequired; } diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index d4129b6..96f0add 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -60,7 +60,6 @@ public: void pushLayerUpdate(DeferredLayerUpdater* layer); void removeLayerUpdate(DeferredLayerUpdater* layer); - void setDirty(int left, int top, int right, int bottom); void setDensity(float density) { mDensity = density; } int drawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos); @@ -80,7 +79,6 @@ private: /********************************************* * Single frame data *********************************************/ - Rect mDirty; nsecs_t mFrameTimeNanos; nsecs_t mRecordDurationNanos; float mDensity; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 0901963..ded10a1 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -180,8 +180,7 @@ void RenderProxy::setOpaque(bool opaque) { } int RenderProxy::syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom) { - mDrawFrameTask.setDirty(dirtyLeft, dirtyTop, dirtyRight, dirtyBottom); + float density) { mDrawFrameTask.setDensity(density); return mDrawFrameTask.drawFrame(frameTimeNanos, recordDurationNanos); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 944ff9c..a95f8f0 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -70,7 +70,7 @@ public: ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius); ANDROID_API void setOpaque(bool opaque); ANDROID_API int syncAndDrawFrame(nsecs_t frameTimeNanos, nsecs_t recordDurationNanos, - float density, int dirtyLeft, int dirtyTop, int dirtyRight, int dirtyBottom); + float density); ANDROID_API void destroyCanvasAndSurface(); ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion); |