diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/Animator.cpp | 145 | ||||
| -rw-r--r-- | libs/hwui/Animator.h | 102 | ||||
| -rw-r--r-- | libs/hwui/DeferredDisplayList.cpp | 11 | ||||
| -rw-r--r-- | libs/hwui/DeferredDisplayList.h | 1 | ||||
| -rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 3 | ||||
| -rw-r--r-- | libs/hwui/Layer.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/LayerRenderer.cpp | 2 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 113 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 15 | ||||
| -rw-r--r-- | libs/hwui/Outline.h | 13 | ||||
| -rw-r--r-- | libs/hwui/Program.h | 28 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.cpp | 50 | ||||
| -rw-r--r-- | libs/hwui/Rect.h | 2 | ||||
| -rw-r--r-- | libs/hwui/RenderNode.cpp | 43 | ||||
| -rw-r--r-- | libs/hwui/RenderNode.h | 28 | ||||
| -rw-r--r-- | libs/hwui/RenderProperties.cpp | 42 | ||||
| -rw-r--r-- | libs/hwui/RenderProperties.h | 12 | ||||
| -rw-r--r-- | libs/hwui/ShadowTessellator.cpp | 4 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.cpp | 58 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.h | 96 | ||||
| -rw-r--r-- | libs/hwui/StatefulBaseRenderer.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/StatefulBaseRenderer.h | 15 | ||||
| -rw-r--r-- | libs/hwui/TreeInfo.h | 4 | ||||
| -rw-r--r-- | libs/hwui/renderthread/CanvasContext.cpp | 7 | ||||
| -rw-r--r-- | libs/hwui/utils/MathUtils.h | 4 |
25 files changed, 495 insertions, 329 deletions
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index a033f86..83eedfb 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -27,31 +27,48 @@ namespace android { namespace uirenderer { /************************************************************ - * Base animator + * BaseRenderNodeAnimator ************************************************************/ -BaseAnimator::BaseAnimator() - : mInterpolator(0) - , mPlayState(PENDING) +BaseRenderNodeAnimator::BaseRenderNodeAnimator(float finalValue) + : mFinalValue(finalValue) + , mDeltaValue(0) + , mFromValue(0) + , mInterpolator(0) + , mPlayState(NEEDS_START) , mStartTime(0) - , mDuration(300) { - + , mDuration(300){ } -BaseAnimator::~BaseAnimator() { +BaseRenderNodeAnimator::~BaseRenderNodeAnimator() { setInterpolator(NULL); } -void BaseAnimator::setInterpolator(Interpolator* interpolator) { +void BaseRenderNodeAnimator::setInterpolator(Interpolator* interpolator) { delete mInterpolator; mInterpolator = interpolator; } -void BaseAnimator::setDuration(nsecs_t duration) { +void BaseRenderNodeAnimator::setDuration(nsecs_t duration) { mDuration = duration; } -bool BaseAnimator::animateFrame(TreeInfo& info) { +void BaseRenderNodeAnimator::setStartValue(float value) { + LOG_ALWAYS_FATAL_IF(mPlayState != NEEDS_START, + "Cannot set the start value after the animator has started!"); + mFromValue = value; + mDeltaValue = (mFinalValue - mFromValue); + mPlayState = PENDING; +} + +void BaseRenderNodeAnimator::setupStartValueIfNecessary(RenderNode* target, TreeInfo& info) { + if (mPlayState == NEEDS_START) { + setStartValue(getValue(target)); + mPlayState = PENDING; + } +} + +bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { if (mPlayState == PENDING) { mPlayState = RUNNING; mStartTime = info.frameTimeMs; @@ -59,7 +76,6 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { if (!mInterpolator) { setInterpolator(Interpolator::createDefaultInterpolator()); } - onAnimationStarted(); } float fraction = 1.0f; @@ -71,17 +87,16 @@ bool BaseAnimator::animateFrame(TreeInfo& info) { } } fraction = mInterpolator->interpolate(fraction); - onAnimationUpdated(fraction); + setValue(target, mFromValue + (mDeltaValue * fraction)); if (mPlayState == FINISHED) { - onAnimationFinished(); callOnFinishedListener(info); return true; } return false; } -void BaseAnimator::callOnFinishedListener(TreeInfo& info) { +void BaseRenderNodeAnimator::callOnFinishedListener(TreeInfo& info) { if (mListener.get()) { if (!info.animationHook) { mListener->onAnimationFinished(this); @@ -92,70 +107,49 @@ void BaseAnimator::callOnFinishedListener(TreeInfo& info) { } /************************************************************ - * BaseRenderNodeAnimator - ************************************************************/ - -BaseRenderNodeAnimator::BaseRenderNodeAnimator( - BaseRenderNodeAnimator::DeltaValueType deltaType, float delta) - : mTarget(0) - , mDeltaValueType(deltaType) - , mDeltaValue(delta) - , mFromValue(-1) { -} - -bool BaseRenderNodeAnimator::animate(RenderNode* target, TreeInfo& info) { - mTarget = target; - bool finished = animateFrame(info); - mTarget = NULL; - return finished; -} - -void BaseRenderNodeAnimator::onAnimationStarted() { - mFromValue = getValue(); - - if (mDeltaValueType == BaseRenderNodeAnimator::ABSOLUTE) { - mDeltaValue = (mDeltaValue - mFromValue); - mDeltaValueType = BaseRenderNodeAnimator::DELTA; - } -} - -void BaseRenderNodeAnimator::onAnimationUpdated(float fraction) { - float value = mFromValue + (mDeltaValue * fraction); - setValue(value); -} - -/************************************************************ * RenderPropertyAnimator ************************************************************/ +struct RenderPropertyAnimator::PropertyAccessors { + RenderNode::DirtyPropertyMask dirtyMask; + GetFloatProperty getter; + SetFloatProperty setter; +}; + // Maps RenderProperty enum to accessors const RenderPropertyAnimator::PropertyAccessors RenderPropertyAnimator::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 }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationX, &RenderProperties::setTranslationX }, + {RenderNode::TRANSLATION_Y, &RenderProperties::getTranslationY, &RenderProperties::setTranslationY }, + {RenderNode::TRANSLATION_X, &RenderProperties::getTranslationZ, &RenderProperties::setTranslationZ }, + {RenderNode::SCALE_X, &RenderProperties::getScaleX, &RenderProperties::setScaleX }, + {RenderNode::SCALE_Y, &RenderProperties::getScaleY, &RenderProperties::setScaleY }, + {RenderNode::ROTATION, &RenderProperties::getRotation, &RenderProperties::setRotation }, + {RenderNode::ROTATION_X, &RenderProperties::getRotationX, &RenderProperties::setRotationX }, + {RenderNode::ROTATION_Y, &RenderProperties::getRotationY, &RenderProperties::setRotationY }, + {RenderNode::X, &RenderProperties::getX, &RenderProperties::setX }, + {RenderNode::Y, &RenderProperties::getY, &RenderProperties::setY }, + {RenderNode::Z, &RenderProperties::getZ, &RenderProperties::setZ }, + {RenderNode::ALPHA, &RenderProperties::getAlpha, &RenderProperties::setAlpha }, }; -RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) - , mPropertyAccess(PROPERTY_ACCESSOR_LUT[property]) { +RenderPropertyAnimator::RenderPropertyAnimator(RenderProperty property, float finalValue) + : BaseRenderNodeAnimator(finalValue) + , mPropertyAccess(&(PROPERTY_ACCESSOR_LUT[property])) { +} + +void RenderPropertyAnimator::onAttached(RenderNode* target) { + if (target->isPropertyFieldDirty(mPropertyAccess->dirtyMask)) { + setStartValue((target->stagingProperties().*mPropertyAccess->getter)()); + } + (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); } -float RenderPropertyAnimator::getValue() const { - return (target()->animatorProperties().*mPropertyAccess.getter)(); +float RenderPropertyAnimator::getValue(RenderNode* target) const { + return (target->properties().*mPropertyAccess->getter)(); } -void RenderPropertyAnimator::setValue(float value) { - (target()->animatorProperties().*mPropertyAccess.setter)(value); +void RenderPropertyAnimator::setValue(RenderNode* target, float value) { + (target->animatorProperties().*mPropertyAccess->setter)(value); } /************************************************************ @@ -163,16 +157,16 @@ void RenderPropertyAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPrimitiveAnimator::CanvasPropertyPrimitiveAnimator( - CanvasPropertyPrimitive* property, DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPrimitive* property, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) { } -float CanvasPropertyPrimitiveAnimator::getValue() const { +float CanvasPropertyPrimitiveAnimator::getValue(RenderNode* target) const { return mProperty->value; } -void CanvasPropertyPrimitiveAnimator::setValue(float value) { +void CanvasPropertyPrimitiveAnimator::setValue(RenderNode* target, float value) { mProperty->value = value; } @@ -181,14 +175,13 @@ void CanvasPropertyPrimitiveAnimator::setValue(float value) { ************************************************************/ CanvasPropertyPaintAnimator::CanvasPropertyPaintAnimator( - CanvasPropertyPaint* property, PaintField field, - DeltaValueType deltaType, float deltaValue) - : BaseRenderNodeAnimator(deltaType, deltaValue) + CanvasPropertyPaint* property, PaintField field, float finalValue) + : BaseRenderNodeAnimator(finalValue) , mProperty(property) , mField(field) { } -float CanvasPropertyPaintAnimator::getValue() const { +float CanvasPropertyPaintAnimator::getValue(RenderNode* target) const { switch (mField) { case STROKE_WIDTH: return mProperty->value.getStrokeWidth(); @@ -204,7 +197,7 @@ static uint8_t to_uint8(float value) { return static_cast<uint8_t>( c < 0 ? 0 : c > 255 ? 255 : c ); } -void CanvasPropertyPaintAnimator::setValue(float value) { +void CanvasPropertyPaintAnimator::setValue(RenderNode* target, float value) { switch (mField) { case STROKE_WIDTH: mProperty->value.setStrokeWidth(value); diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index 52a1807..fe88cbf 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -33,16 +33,14 @@ class RenderProperties; class AnimationListener : public VirtualLightRefBase { public: - ANDROID_API virtual void onAnimationFinished(BaseAnimator*) = 0; + ANDROID_API virtual void onAnimationFinished(BaseRenderNodeAnimator*) = 0; protected: ANDROID_API virtual ~AnimationListener() {} }; -// Helper class to contain generic animator helpers -class BaseAnimator : public VirtualLightRefBase { - PREVENT_COPY_AND_ASSIGN(BaseAnimator); +class BaseRenderNodeAnimator : public VirtualLightRefBase { + PREVENT_COPY_AND_ASSIGN(BaseRenderNodeAnimator); public: - ANDROID_API void setInterpolator(Interpolator* interpolator); ANDROID_API void setDuration(nsecs_t durationInMs); ANDROID_API nsecs_t duration() { return mDuration; } @@ -50,31 +48,38 @@ public: mListener = listener; } + ANDROID_API virtual void onAttached(RenderNode* target) {} + + // Guaranteed to happen before the staging push + void setupStartValueIfNecessary(RenderNode* target, TreeInfo& info); + + bool animate(RenderNode* target, TreeInfo& info); + bool isFinished() { return mPlayState == FINISHED; } + float finalValue() { return mFinalValue; } protected: - BaseAnimator(); - virtual ~BaseAnimator(); - - // 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(TreeInfo& info); + BaseRenderNodeAnimator(float finalValue); + virtual ~BaseRenderNodeAnimator(); - // Called when PlayState switches from PENDING to RUNNING - virtual void onAnimationStarted() {} - virtual void onAnimationUpdated(float fraction) = 0; - virtual void onAnimationFinished() {} + void setStartValue(float value); + virtual float getValue(RenderNode* target) const = 0; + virtual void setValue(RenderNode* target, float value) = 0; private: void callOnFinishedListener(TreeInfo& info); enum PlayState { + NEEDS_START, PENDING, RUNNING, FINISHED, }; + float mFinalValue; + float mDeltaValue; + float mFromValue; + Interpolator* mInterpolator; PlayState mPlayState; long mStartTime; @@ -83,42 +88,6 @@ private: sp<AnimationListener> mListener; }; -class BaseRenderNodeAnimator : public BaseAnimator { -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, - }; - - bool animate(RenderNode* target, TreeInfo& info); - -protected: - BaseRenderNodeAnimator(DeltaValueType deltaType, float deltaValue); - - RenderNode* target() const { return mTarget; } - virtual float getValue() const = 0; - virtual void setValue(float value) = 0; - -private: - virtual void onAnimationStarted(); - virtual void onAnimationUpdated(float fraction); - - // mTarget is only valid inside animate() - RenderNode* mTarget; - - BaseRenderNodeAnimator::DeltaValueType mDeltaValueType; - float mDeltaValue; - float mFromValue; -}; - class RenderPropertyAnimator : public BaseRenderNodeAnimator { public: enum RenderProperty { @@ -136,23 +105,20 @@ public: ALPHA, }; - ANDROID_API RenderPropertyAnimator(RenderProperty property, - DeltaValueType deltaType, float deltaValue); + ANDROID_API RenderPropertyAnimator(RenderProperty property, float finalValue); + + ANDROID_API virtual void onAttached(RenderNode* target); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: typedef void (RenderProperties::*SetFloatProperty)(float value); typedef float (RenderProperties::*GetFloatProperty)() const; - struct PropertyAccessors { - GetFloatProperty getter; - SetFloatProperty setter; - }; - - PropertyAccessors mPropertyAccess; + struct PropertyAccessors; + const PropertyAccessors* mPropertyAccess; static const PropertyAccessors PROPERTY_ACCESSOR_LUT[]; }; @@ -160,10 +126,10 @@ private: class CanvasPropertyPrimitiveAnimator : public BaseRenderNodeAnimator { public: ANDROID_API CanvasPropertyPrimitiveAnimator(CanvasPropertyPrimitive* property, - DeltaValueType deltaType, float deltaValue); + float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPrimitive> mProperty; }; @@ -176,10 +142,10 @@ public: }; ANDROID_API CanvasPropertyPaintAnimator(CanvasPropertyPaint* property, - PaintField field, DeltaValueType deltaType, float deltaValue); + PaintField field, float finalValue); protected: - ANDROID_API virtual float getValue() const; - ANDROID_API virtual void setValue(float value); + virtual float getValue(RenderNode* target) const; + virtual void setValue(RenderNode* target, float value); private: sp<CanvasPropertyPaint> mProperty; PaintField mField; diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 45b6624..3016814 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -28,6 +28,7 @@ #include "DeferredDisplayList.h" #include "DisplayListOp.h" #include "OpenGLRenderer.h" +#include "utils/MathUtils.h" #if DEBUG_DEFER #define DEFER_LOGD(...) ALOGD(__VA_ARGS__) @@ -146,10 +147,6 @@ private: mergeid_t mMergeId; }; -// compare alphas approximately, with a small margin -#define NEQ_FALPHA(lhs, rhs) \ - fabs((float)lhs - (float)rhs) > 0.001f - class MergingDrawBatch : public DrawBatch { public: MergingDrawBatch(DeferInfo& deferInfo, int width, int height) : @@ -196,7 +193,11 @@ public: const DeferredDisplayState* lhs = state; const DeferredDisplayState* rhs = mOps[0].state; - if (NEQ_FALPHA(lhs->mAlpha, rhs->mAlpha)) return false; + if (!MathUtils::areEqual(lhs->mAlpha, rhs->mAlpha)) return false; + + // Identical round rect clip state means both ops will clip in the same way, or not at all. + // As the state objects are const, we can compare their pointers to determine mergeability + if (lhs->mRoundRectClipState != rhs->mRoundRectClipState) return false; /* Clipping compatibility check * diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index fca3588..48489c2 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -64,6 +64,7 @@ public: mat4 mMatrix; DrawModifiers mDrawModifiers; float mAlpha; + const RoundRectClipState* mRoundRectClipState; }; class OpStatePair { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 2391e80..a4bce3a 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -57,9 +57,6 @@ DisplayListData* DisplayListRenderer::finishRecording() { } void DisplayListRenderer::setViewport(int width, int height) { - // TODO: DisplayListRenderer shouldn't have a projection matrix, as it should never be used - mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - initializeViewport(width, height); } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 9606e58..de2fcf4 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -214,7 +214,7 @@ void Layer::defer() { DeferStateStruct deferredState(*deferredList, *renderer, RenderNode::kReplayFlag_ClipChildren); - renderer->initViewport(width, height); + renderer->initializeViewport(width, height); renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index e0ac2ba..c82197c 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -40,7 +40,7 @@ LayerRenderer::~LayerRenderer() { } void LayerRenderer::setViewport(int width, int height) { - initViewport(width, height); + initializeViewport(width, height); } status_t LayerRenderer::prepareDirty(float left, float top, float right, float bottom, diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 20b038d..7993c0f 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -162,7 +162,7 @@ void OpenGLRenderer::initProperties() { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::setViewport(int width, int height) { - initViewport(width, height); + initializeViewport(width, height); glDisable(GL_DITHER); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -170,12 +170,6 @@ void OpenGLRenderer::setViewport(int width, int height) { glEnableVertexAttribArray(Program::kBindingPosition); } -void OpenGLRenderer::initViewport(int width, int height) { - mProjectionMatrix.loadOrtho(0, width, height, 0, -1, 1); - - initializeViewport(width, height); -} - void OpenGLRenderer::setupFrameState(float left, float top, float right, float bottom, bool opaque) { mCaches.clearGarbage(); @@ -244,7 +238,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { if (!opaque || mCountOverdraw) { mCaches.enableScissor(); - mCaches.setScissor(left, currentSnapshot()->height - bottom, right - left, bottom - top); + mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); return DrawGlInfo::kStatusDrew; } @@ -270,7 +264,7 @@ void OpenGLRenderer::startTilingCurrentClip(bool opaque) { clip = &(snapshot->layer->clipRect); } - startTiling(*clip, snapshot->height, opaque); + startTiling(*clip, getViewportHeight(), opaque); } } @@ -333,7 +327,7 @@ void OpenGLRenderer::interrupt() { void OpenGLRenderer::resume() { const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); + glViewport(0, 0, getViewportWidth(), getViewportHeight()); glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); debugOverdraw(true, false); @@ -354,9 +348,8 @@ void OpenGLRenderer::resume() { } void OpenGLRenderer::resumeAfterLayer() { - const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, snapshot->viewport.getWidth(), snapshot->viewport.getHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); + glViewport(0, 0, getViewportWidth(), getViewportHeight()); + glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo); debugOverdraw(true, false); mCaches.resetScissor(); @@ -381,8 +374,8 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.clipRight = clip.right; info.clipBottom = clip.bottom; info.isLayer = hasLayer(); - info.width = currentSnapshot()->viewport.getWidth(); - info.height = currentSnapshot()->height; + info.width = getViewportWidth(); + info.height = getViewportHeight(); currentTransform()->copyTo(&info.transform[0]); bool dirtyClip = mDirtyClip; @@ -437,7 +430,7 @@ void OpenGLRenderer::renderOverdraw() { const Rect* clip = &mTilingClip; mCaches.enableScissor(); - mCaches.setScissor(clip->left, firstSnapshot()->height - clip->bottom, + mCaches.setScissor(clip->left, firstSnapshot()->getViewportHeight() - clip->bottom, clip->right - clip->left, clip->bottom - clip->top); // 1x overdraw @@ -621,14 +614,12 @@ void OpenGLRenderer::flushLayerUpdates() { /////////////////////////////////////////////////////////////////////////////// void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) { - bool restoreOrtho = removed.flags & Snapshot::kFlagDirtyOrtho; + bool restoreViewport = removed.flags & Snapshot::kFlagIsFboLayer; bool restoreClip = removed.flags & Snapshot::kFlagClipSet; bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; - if (restoreOrtho) { - const Rect& r = restored.viewport; - glViewport(r.left, r.top, r.right, r.bottom); - mProjectionMatrix.load(removed.orthoMatrix); // TODO: should ortho be stored in 'restored'? + if (restoreViewport) { + glViewport(0, 0, getViewportWidth(), getViewportHeight()); } if (restoreClip) { @@ -671,7 +662,7 @@ void OpenGLRenderer::calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool // When the layer is not an FBO, we may use glCopyTexImage so we // need to make sure the layer does not extend outside the bounds // of the framebuffer - if (!bounds.intersect(currentSnapshot()->previous->viewport)) { + if (!bounds.intersect(Rect(0, 0, getViewportWidth(), getViewportHeight()))) { bounds.setEmpty(); } else if (fboLayer) { clip.set(bounds); @@ -719,7 +710,8 @@ int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float if (!currentSnapshot()->isIgnored()) { mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); + mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); + mSnapshot->roundRectClipState = NULL; } } @@ -831,8 +823,9 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto layer->setEmpty(false); } - glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, bounds.left, - mSnapshot->height - bounds.bottom, bounds.getWidth(), bounds.getHeight()); + glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, + bounds.left, getViewportHeight() - bounds.bottom, + bounds.getWidth(), bounds.getHeight()); // Enqueue the buffer coordinates to clear the corresponding region later mLayers.push(new Rect(bounds)); @@ -847,14 +840,12 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { layer->setFbo(mCaches.fboCache.get()); mSnapshot->region = &mSnapshot->layer->region; - mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer | - Snapshot::kFlagDirtyOrtho; + mSnapshot->flags |= Snapshot::kFlagFboTarget | Snapshot::kFlagIsFboLayer; mSnapshot->fbo = layer->getFbo(); mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom); - mSnapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - mSnapshot->height = bounds.getHeight(); - mSnapshot->orthoMatrix.load(mProjectionMatrix); + mSnapshot->initializeViewport(bounds.getWidth(), bounds.getHeight()); + mSnapshot->roundRectClipState = NULL; endTiling(); debugOverdraw(false, false); @@ -883,9 +874,6 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { // Change the ortho projection glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - - mProjectionMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); - return true; } @@ -904,7 +892,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto bool clipRequired = false; calculateQuickRejectForScissor(rect.left, rect.top, rect.right, rect.bottom, - &clipRequired, false); // safely ignore return, should never be rejected + &clipRequired, NULL, false); // safely ignore return, should never be rejected mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); if (fboLayer) { @@ -1379,6 +1367,9 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef state.mMatrix.load(*currentMatrix); state.mDrawModifiers = mDrawModifiers; state.mAlpha = currentSnapshot()->alpha; + + // always store/restore, since it's just a pointer + state.mRoundRectClipState = currentSnapshot()->roundRectClipState; return false; } @@ -1386,6 +1377,7 @@ void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, bool setMatrix(state.mMatrix); mSnapshot->alpha = state.mAlpha; mDrawModifiers = state.mDrawModifiers; + mSnapshot->roundRectClipState = state.mRoundRectClipState; if (state.mClipValid && !skipClipRestore) { mSnapshot->setClip(state.mClip.left, state.mClip.top, @@ -1419,7 +1411,7 @@ void OpenGLRenderer::setScissorFromClip() { Rect clip(*currentClipRect()); clip.snapToPixelBoundaries(); - if (mCaches.setScissor(clip.left, currentSnapshot()->height - clip.bottom, + if (mCaches.setScissor(clip.left, getViewportHeight() - clip.bottom, clip.getWidth(), clip.getHeight())) { mDirtyClip = false; } @@ -1461,7 +1453,7 @@ void OpenGLRenderer::setStencilFromClip() { mCaches.stencil.enableWrite(); - // Clear the stencil but first make sure we restrict drawing + // Clear and update the stencil, but first make sure we restrict drawing // to the region's bounds bool resetScissor = mCaches.enableScissor(); if (resetScissor) { @@ -1469,7 +1461,10 @@ void OpenGLRenderer::setStencilFromClip() { setScissorFromClip(); } mCaches.stencil.clear(); - if (resetScissor) mCaches.disableScissor(); + + // stash and disable the outline clip state, since stencil doesn't account for outline + bool storedSkipOutlineClip = mSkipOutlineClip; + mSkipOutlineClip = true; SkPaint paint; paint.setColor(0xff000000); @@ -1482,6 +1477,8 @@ void OpenGLRenderer::setStencilFromClip() { // The last parameter is important: we are not drawing in the color buffer // so we don't want to dirty the current layer, if any drawRegionRects(*(currentSnapshot()->clipRegion), paint, false); + if (resetScissor) mCaches.disableScissor(); + mSkipOutlineClip = storedSkipOutlineClip; mCaches.stencil.enableTest(); @@ -1506,7 +1503,6 @@ void OpenGLRenderer::setStencilFromClip() { */ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, float bottom, const SkPaint* paint) { - bool clipRequired = false; bool snapOut = paint && paint->isAntiAlias(); if (paint && paint->getStyle() != SkPaint::kFill_Style) { @@ -1517,13 +1513,17 @@ bool OpenGLRenderer::quickRejectSetupScissor(float left, float top, float right, bottom += outset; } - if (calculateQuickRejectForScissor(left, top, right, bottom, &clipRequired, snapOut)) { + bool clipRequired = false; + bool roundRectClipRequired = false; + if (calculateQuickRejectForScissor(left, top, right, bottom, + &clipRequired, &roundRectClipRequired, snapOut)) { return true; } if (!isRecording()) { // not quick rejected, so enable the scissor if clipRequired mCaches.setScissorEnabled(mScissorOptimizationDisabled || clipRequired); + mSkipOutlineClip = !roundRectClipRequired; } return false; } @@ -1680,6 +1680,18 @@ void OpenGLRenderer::setupDrawBlending(const SkPaint* paint, bool blend, bool sw void OpenGLRenderer::setupDrawProgram() { useProgram(mCaches.programCache.get(mDescription)); + if (mDescription.hasRoundRectClip) { + // TODO: avoid doing this repeatedly, stashing state pointer in program + const RoundRectClipState* state = mSnapshot->roundRectClipState; + const Rect& innerRect = state->outlineInnerRect; + glUniform4f(mCaches.currentProgram->getUniform("roundRectInnerRectLTRB"), + innerRect.left, innerRect.top, + innerRect.right, innerRect.bottom); + glUniform1f(mCaches.currentProgram->getUniform("roundRectRadius"), + state->outlineRadius); + glUniformMatrix4fv(mCaches.currentProgram->getUniform("roundRectInvTransform"), + 1, GL_FALSE, &state->matrix.data[0]); + } } void OpenGLRenderer::setupDrawDirtyRegionsDisabled() { @@ -1694,12 +1706,14 @@ void OpenGLRenderer::setupDrawModelView(ModelViewMode mode, bool offset, } bool dirty = right - left > 0.0f && bottom - top > 0.0f; - if (!ignoreTransform) { - mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, *currentTransform(), offset); - if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom, *currentTransform()); - } else { - mCaches.currentProgram->set(mProjectionMatrix, mModelViewMatrix, mat4::identity(), offset); - if (dirty && mTrackDirtyRegions) dirtyLayer(left, top, right, bottom); + const Matrix4& transformMatrix = ignoreTransform ? Matrix4::identity() : *currentTransform(); + mCaches.currentProgram->set(mSnapshot->getOrthoMatrix(), mModelViewMatrix, transformMatrix, offset); + if (dirty && mTrackDirtyRegions) { + if (!ignoreTransform) { + dirtyLayer(left, top, right, bottom, *currentTransform()); + } else { + dirtyLayer(left, top, right, bottom); + } } } @@ -2912,7 +2926,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) { bool clipRequired = false; const bool rejected = calculateQuickRejectForScissor(x, y, - x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, false); + x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired, NULL, false); if (rejected) { if (transform && !transform->isIdentity()) { @@ -3443,6 +3457,13 @@ void OpenGLRenderer::drawAlpha8TextureMesh(float left, float top, float right, f void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, bool swapSrcDst) { + + if (mSnapshot->roundRectClipState != NULL /*&& !mSkipOutlineClip*/) { + blend = true; + mDescription.hasRoundRectClip = true; + } + mSkipOutlineClip = true; + if (mCountOverdraw) { if (!mCaches.blend) glEnable(GL_BLEND); if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 4f7f01e..f70ae58 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -254,8 +254,8 @@ public: return mSnapshot->clipRegion->isEmpty(); } - int getViewportWidth() { return currentSnapshot()->viewport.getWidth(); } - int getViewportHeight() { return currentSnapshot()->viewport.getHeight(); } + int getViewportWidth() { return currentSnapshot()->getViewportWidth(); } + int getViewportHeight() { return currentSnapshot()->getViewportHeight(); } /** * Scales the alpha on the current snapshot. This alpha value will be modulated @@ -354,12 +354,6 @@ public: protected: /** - * Computes the projection matrix, initialize the first snapshot - * and stores the dimensions of the render target. - */ - void initViewport(int width, int height); - - /** * Perform the setup specific to a frame. This method does not * issue any OpenGL commands. */ @@ -930,9 +924,6 @@ private: */ Texture* getTexture(const SkBitmap* bitmap); - // Ortho matrix used for projection in shaders - mat4 mProjectionMatrix; - /** * Model-view matrix used to position/size objects * @@ -1002,6 +993,8 @@ private: bool mCountOverdraw; float mOverdraw; + bool mSkipOutlineClip; + friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; diff --git a/libs/hwui/Outline.h b/libs/hwui/Outline.h index 530be30..5c24335 100644 --- a/libs/hwui/Outline.h +++ b/libs/hwui/Outline.h @@ -58,11 +58,24 @@ public: mShouldClip = clip; } + bool getShouldClip() const { + return mShouldClip; + } + bool willClip() const { // only round rect outlines can be used for clipping return mShouldClip && (mType == kOutlineType_RoundRect); } + bool getAsRoundRect(Rect* outRect, float* outRadius) const { + if (mType == kOutlineType_RoundRect) { + outRect->set(mBounds); + *outRadius = mRadius; + return true; + } + return false; + } + const SkPath* getPath() const { if (mType == kOutlineType_None) return NULL; diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 33c91b3..3e191d0 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -45,17 +45,18 @@ namespace uirenderer { #define COLOR_COMPONENT_THRESHOLD 1.0f #define COLOR_COMPONENT_INV_THRESHOLD 0.0f -#define PROGRAM_KEY_TEXTURE 0x1 -#define PROGRAM_KEY_A8_TEXTURE 0x2 -#define PROGRAM_KEY_BITMAP 0x4 -#define PROGRAM_KEY_GRADIENT 0x8 -#define PROGRAM_KEY_BITMAP_FIRST 0x10 -#define PROGRAM_KEY_COLOR_MATRIX 0x20 -#define PROGRAM_KEY_COLOR_BLEND 0x40 -#define PROGRAM_KEY_BITMAP_NPOT 0x80 -#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 - -#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 +#define PROGRAM_KEY_TEXTURE 0x01 +#define PROGRAM_KEY_A8_TEXTURE 0x02 +#define PROGRAM_KEY_BITMAP 0x04 +#define PROGRAM_KEY_GRADIENT 0x08 +#define PROGRAM_KEY_BITMAP_FIRST 0x10 +#define PROGRAM_KEY_COLOR_MATRIX 0x20 +#define PROGRAM_KEY_COLOR_BLEND 0x40 +#define PROGRAM_KEY_BITMAP_NPOT 0x80 + +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 + +#define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 // Encode the xfermodes on 6 bits @@ -83,6 +84,7 @@ namespace uirenderer { #define PROGRAM_HAS_DEBUG_HIGHLIGHT 42 #define PROGRAM_EMULATE_STENCIL 43 +#define PROGRAM_HAS_ROUND_RECT_CLIP 44 /////////////////////////////////////////////////////////////////////////////// // Types @@ -158,6 +160,7 @@ struct ProgramDescription { bool hasDebugHighlight; bool emulateStencil; + bool hasRoundRectClip; /** * Resets this description. All fields are reset back to the default @@ -198,6 +201,8 @@ struct ProgramDescription { gamma = 2.2f; hasDebugHighlight = false; + emulateStencil = false; + hasRoundRectClip = false; } /** @@ -264,6 +269,7 @@ struct ProgramDescription { if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL; + if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; return key; } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 6d50410..f451690 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -58,6 +58,8 @@ const char* gVS_Header_Uniforms_HasGradient = const char* gVS_Header_Uniforms_HasBitmap = "uniform mat4 textureTransform;\n" "uniform mediump vec2 textureDimension;\n"; +const char* gVS_Header_Uniforms_HasRoundRectClip = + "uniform mat4 roundRectInvTransform;\n"; const char* gVS_Header_Varyings_HasTexture = "varying vec2 outTexCoords;\n"; const char* gVS_Header_Varyings_HasColors = @@ -85,6 +87,8 @@ const char* gVS_Header_Varyings_HasGradient[6] = { "varying highp vec2 sweep;\n" "varying vec2 ditherTexCoords;\n", }; +const char* gVS_Header_Varyings_HasRoundRectClip = + "varying vec2 roundRectPos;\n"; const char* gVS_Main = "\nvoid main(void) {\n"; const char* gVS_Main_OutTexCoords = @@ -115,9 +119,12 @@ const char* gVS_Main_OutGradient[6] = { const char* gVS_Main_OutBitmapTexCoords = " outBitmapTexCoords = (textureTransform * position).xy * textureDimension;\n"; const char* gVS_Main_Position = - " gl_Position = projection * transform * position;\n"; + " vec4 transformedPosition = projection * transform * position;\n" + " gl_Position = transformedPosition;\n"; const char* gVS_Main_AAVertexShape = " alpha = vtxAlpha;\n"; +const char* gVS_Main_HasRoundRectClip = + " roundRectPos = (roundRectInvTransform * transformedPosition).xy;\n"; const char* gVS_Footer = "}\n\n"; @@ -160,6 +167,10 @@ const char* gFS_Uniforms_ColorOp[3] = { const char* gFS_Uniforms_Gamma = "uniform float gamma;\n"; +const char* gFS_Uniforms_HasRoundRectClip = + "uniform vec4 roundRectInnerRectLTRB;\n" + "uniform float roundRectRadius;\n"; + const char* gFS_Main = "\nvoid main(void) {\n" " lowp vec4 fragColor;\n"; @@ -318,6 +329,15 @@ const char* gFS_Main_ApplyColorOp[3] = { // PorterDuff " fragColor = blendColors(colorBlend, fragColor);\n" }; + +// Note: LTRB -> xyzw +const char* gFS_Main_FragColor_HasRoundRectClip = + " mediump vec2 fragToLT = roundRectInnerRectLTRB.xy - roundRectPos;\n" + " mediump vec2 fragFromRB = roundRectPos - roundRectInnerRectLTRB.zw;\n" + " mediump vec2 dist = max(max(fragToLT, fragFromRB), vec2(0.0, 0.0));\n" + " mediump float linearDist = roundRectRadius - length(dist);\n" + " gl_FragColor *= clamp(linearDist, 0.0, 1.0);\n"; + const char* gFS_Main_DebugHighlight = " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; const char* gFS_Main_EmulateStencil = @@ -462,6 +482,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasBitmap) { shader.append(gVS_Header_Uniforms_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Uniforms_HasRoundRectClip); + } // Varyings if (description.hasTexture || description.hasExternalTexture) { shader.append(gVS_Header_Varyings_HasTexture); @@ -478,6 +501,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasBitmap) { shader.append(gVS_Header_Varyings_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Varyings_HasRoundRectClip); + } // Begin the shader shader.append(gVS_Main); { @@ -500,6 +526,9 @@ String8 ProgramCache::generateVertexShader(const ProgramDescription& description if (description.hasGradient) { shader.append(gVS_Main_OutGradient[gradientIndex(description)]); } + if (description.hasRoundRectClip) { + shader.append(gVS_Main_HasRoundRectClip); + } } // End the shader shader.append(gVS_Footer); @@ -546,6 +575,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasBitmap) { shader.append(gVS_Header_Varyings_HasBitmap); } + if (description.hasRoundRectClip) { + shader.append(gVS_Header_Varyings_HasRoundRectClip); + } // Uniforms int modulateOp = MODULATE_OP_NO_MODULATE; @@ -568,11 +600,18 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasGammaCorrection) { shader.append(gFS_Uniforms_Gamma); } + if (description.hasRoundRectClip) { + shader.append(gFS_Uniforms_HasRoundRectClip); + } // Optimization for common cases - if (!description.isAA && !blendFramebuffer && !description.hasColors && - description.colorOp == ProgramDescription::kColorNone && - !description.hasDebugHighlight && !description.emulateStencil) { + if (!description.isAA + && !blendFramebuffer + && !description.hasColors + && description.colorOp == ProgramDescription::kColorNone + && !description.hasDebugHighlight + && !description.emulateStencil + && !description.hasRoundRectClip) { bool fast = false; const bool noShader = !description.hasGradient && !description.hasBitmap; @@ -722,6 +761,9 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (description.hasColors) { shader.append(gFS_Main_FragColor_HasColors); } + if (description.hasRoundRectClip) { + shader.append(gFS_Main_FragColor_HasRoundRectClip); + } if (description.hasDebugHighlight) { shader.append(gFS_Main_DebugHighlight); } diff --git a/libs/hwui/Rect.h b/libs/hwui/Rect.h index f38d8b7..2ddbbd7 100644 --- a/libs/hwui/Rect.h +++ b/libs/hwui/Rect.h @@ -234,7 +234,7 @@ public: bottom = ceilf(bottom); } - void dump(const char* label) const { + void dump(const char* label = NULL) const { ALOGD("%s[l=%f t=%f r=%f b=%f]", label ? label : "Rect", left, top, right, bottom); } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 9902ff1..f0645a9 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -53,7 +53,7 @@ void RenderNode::outputLogBuffer(int fd) { } RenderNode::RenderNode() - : mNeedsPropertiesSync(false) + : mDirtyPropertyFields(0) , mNeedsDisplayListDataSync(false) , mDisplayListData(0) , mStagingDisplayListData(0) @@ -109,23 +109,37 @@ void RenderNode::prepareTreeImpl(TreeInfo& info) { prepareSubTree(info, mDisplayListData); } -static bool is_finished(const sp<BaseRenderNodeAnimator>& animator) { - return animator->isFinished(); -} +class PushAnimatorsFunctor { +public: + PushAnimatorsFunctor(RenderNode* target, TreeInfo& info) + : mTarget(target), mInfo(info) {} -void RenderNode::pushStagingChanges(TreeInfo& info) { - if (mNeedsPropertiesSync) { - mNeedsPropertiesSync = false; - mProperties = mStagingProperties; + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { + animator->setupStartValueIfNecessary(mTarget, mInfo); + return animator->isFinished(); } +private: + RenderNode* mTarget; + TreeInfo& mInfo; +}; + +void RenderNode::pushStagingChanges(TreeInfo& info) { + // Push the animators first so that setupStartValueIfNecessary() is called + // before properties() is trampled by stagingProperties(), as they are + // required by some animators. if (mNeedsAnimatorsSync) { mAnimators.resize(mStagingAnimators.size()); std::vector< sp<BaseRenderNodeAnimator> >::iterator it; + PushAnimatorsFunctor functor(this, info); // hint: this means copy_if_not() it = std::remove_copy_if(mStagingAnimators.begin(), mStagingAnimators.end(), - mAnimators.begin(), is_finished); + mAnimators.begin(), functor); mAnimators.resize(std::distance(mAnimators.begin(), it)); } + if (mDirtyPropertyFields) { + mDirtyPropertyFields = 0; + mProperties = mStagingProperties; + } if (mNeedsDisplayListDataSync) { mNeedsDisplayListDataSync = false; // Do a push pass on the old tree to handle freeing DisplayListData @@ -144,7 +158,7 @@ public: AnimateFunctor(RenderNode* target, TreeInfo& info) : mTarget(target), mInfo(info) {} - bool operator() (sp<BaseRenderNodeAnimator>& animator) { + bool operator() (const sp<BaseRenderNodeAnimator>& animator) { return animator->animate(mTarget, mInfo); } private: @@ -238,9 +252,8 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { } if (CC_UNLIKELY(properties().hasClippingPath())) { - // TODO: optimize for round rect/circle clipping - const SkPath* path = properties().getClippingPath(); - ClipPathOp* op = new (handler.allocator()) ClipPathOp(path, SkRegion::kIntersect_Op); + ClipPathOp* op = new (handler.allocator()) ClipPathOp( + properties().getClippingPath(), properties().getClippingPathOp()); handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); } } @@ -654,6 +667,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { bool quickRejected = properties().getClipToBounds() && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); if (!quickRejected) { + if (mProperties.getOutline().willClip()) { + renderer.setClippingOutline(alloc, &(mProperties.getOutline())); + } + Vector<ZDrawDisplayListOpPair> zTranslatedNodes; buildZSortedChildList(zTranslatedNodes); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index bc62ee1..1811a7b 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -82,6 +82,22 @@ class DrawDisplayListOp; */ class RenderNode : public VirtualLightRefBase { public: + enum DirtyPropertyMask { + GENERIC = 1 << 1, + TRANSLATION_X = 1 << 2, + TRANSLATION_Y = 1 << 3, + TRANSLATION_Z = 1 << 4, + SCALE_X = 1 << 5, + SCALE_Y = 1 << 6, + ROTATION = 1 << 7, + ROTATION_X = 1 << 8, + ROTATION_Y = 1 << 9, + X = 1 << 10, + Y = 1 << 11, + Z = 1 << 12, + ALPHA = 1 << 13, + }; + ANDROID_API RenderNode(); ANDROID_API virtual ~RenderNode(); @@ -123,6 +139,14 @@ public: } } + bool isPropertyFieldDirty(DirtyPropertyMask field) const { + return mDirtyPropertyFields & field; + } + + void setPropertyFieldsDirty(uint32_t fields) { + mDirtyPropertyFields |= fields; + } + const RenderProperties& properties() { return mProperties; } @@ -136,7 +160,6 @@ public: } RenderProperties& mutateStagingProperties() { - mNeedsPropertiesSync = true; return mStagingProperties; } @@ -152,6 +175,7 @@ public: // UI thread only! ANDROID_API void addAnimator(const sp<BaseRenderNodeAnimator>& animator) { + animator->onAttached(this); mStagingAnimators.insert(animator); mNeedsAnimatorsSync = true; } @@ -227,7 +251,7 @@ private: String8 mName; - bool mNeedsPropertiesSync; + uint32_t mDirtyPropertyFields; RenderProperties mProperties; RenderProperties mStagingProperties; diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 99de1fc..5f7d4e3 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -50,14 +50,11 @@ RenderProperties::PrimitiveFields::PrimitiveFields() } RenderProperties::ComputedFields::ComputedFields() - : mTransformMatrix(NULL) - , mClipPath(NULL) - , mClipPathOp(SkRegion::kIntersect_Op) { + : mTransformMatrix(NULL) { } RenderProperties::ComputedFields::~ComputedFields() { delete mTransformMatrix; - delete mClipPath; } RenderProperties::RenderProperties() @@ -77,9 +74,6 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { setAnimationMatrix(other.getAnimationMatrix()); setCameraDistance(other.getCameraDistance()); - // Update the computed clip path - updateClipPath(); - // Force recalculation of the matrix, since other's dirty bit may be clear mPrimitiveFields.mMatrixOrPivotDirty = true; updateMatrix(); @@ -166,39 +160,5 @@ void RenderProperties::updateMatrix() { } } -void RenderProperties::updateClipPath() { - const SkPath* outlineClipPath = mPrimitiveFields.mOutline.willClip() - ? mPrimitiveFields.mOutline.getPath() : NULL; - const SkPath* revealClipPath = mPrimitiveFields.mRevealClip.getPath(); - - if (!outlineClipPath && !revealClipPath) { - // mComputedFields.mClipPath doesn't need to be updated, since it won't be used - return; - } - - if (mComputedFields.mClipPath == NULL) { - mComputedFields.mClipPath = new SkPath(); - } - SkPath* clipPath = mComputedFields.mClipPath; - mComputedFields.mClipPathOp = SkRegion::kIntersect_Op; - - if (outlineClipPath && revealClipPath) { - SkPathOp op = kIntersect_PathOp; - if (mPrimitiveFields.mRevealClip.isInverseClip()) { - op = kDifference_PathOp; // apply difference step in the Op below, instead of draw time - } - - Op(*outlineClipPath, *revealClipPath, op, clipPath); - } else if (outlineClipPath) { - *clipPath = *outlineClipPath; - } else { - *clipPath = *revealClipPath; - if (mPrimitiveFields.mRevealClip.isInverseClip()) { - // apply difference step at draw time - mComputedFields.mClipPathOp = SkRegion::kDifference_Op; - } - } -} - } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index 6fc8bce..c0e3ce7 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -437,19 +437,17 @@ public: ANDROID_API void updateMatrix(); - ANDROID_API void updateClipPath(); - - // signals that mComputedFields.mClipPath is up to date, and should be used for clipping bool hasClippingPath() const { - return mPrimitiveFields.mOutline.willClip() || mPrimitiveFields.mRevealClip.willClip(); + return mPrimitiveFields.mRevealClip.willClip(); } const SkPath* getClippingPath() const { - return hasClippingPath() ? mComputedFields.mClipPath : NULL; + return mPrimitiveFields.mRevealClip.getPath(); } SkRegion::Op getClippingPathOp() const { - return mComputedFields.mClipPathOp; + return mPrimitiveFields.mRevealClip.isInverseClip() + ? SkRegion::kDifference_Op : SkRegion::kIntersect_Op; } Outline& mutableOutline() { @@ -505,8 +503,6 @@ private: SkMatrix* mTransformMatrix; Sk3DView mTransformCamera; - SkPath* mClipPath; // TODO: remove this, create new ops for efficient/special case clipping - SkRegion::Op mClipPathOp; } mComputedFields; }; diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index be49aed..55b82e4 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -196,6 +196,10 @@ Vector2 ShadowTessellator::centroid2d(const Vector2* poly, int polyLength) { * @param len the number of points of the polygon */ bool ShadowTessellator::isClockwise(const Vector2* polygon, int len) { + if (len < 2 || polygon == NULL) { + ALOGW("Invalid polygon %p, length is %d @ isClockwise()", polygon, len); + return true; + } double sum = 0; double p1x = polygon[len - 1].x; double p1y = polygon[len - 1].y; diff --git a/libs/hwui/Snapshot.cpp b/libs/hwui/Snapshot.cpp index 6bfa203..80f7eca 100644 --- a/libs/hwui/Snapshot.cpp +++ b/libs/hwui/Snapshot.cpp @@ -20,6 +20,8 @@ #include <SkCanvas.h> +#include "utils/MathUtils.h" + namespace android { namespace uirenderer { @@ -34,8 +36,8 @@ Snapshot::Snapshot() , fbo(0) , invisible(false) , empty(false) - , height(0) - , alpha(1.0f) { + , alpha(1.0f) + , roundRectClipState(NULL) { transform = &mTransformRoot; clipRect = &mClipRectRoot; region = NULL; @@ -53,10 +55,9 @@ Snapshot::Snapshot(const sp<Snapshot>& s, int saveFlags) , fbo(s->fbo) , invisible(s->invisible) , empty(false) - , viewport(s->viewport) - , height(s->height) - , alpha(s->alpha) { - + , alpha(s->alpha) + , roundRectClipState(s->roundRectClipState) + , mViewportData(s->mViewportData) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -206,6 +207,49 @@ void Snapshot::resetTransform(float x, float y, float z) { } /////////////////////////////////////////////////////////////////////////////// +// Clipping outline +/////////////////////////////////////////////////////////////////////////////// + +void Snapshot::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { + Rect bounds; + float radius; + if (!outline->getAsRoundRect(&bounds, &radius)) return; // only RR supported + + if (!MathUtils::isPositive(radius)) return; // leave clipping up to rect clipping + + RoundRectClipState* state = new (allocator) RoundRectClipState; + + // store the inverse drawing matrix + Matrix4 outlineDrawingMatrix; + outlineDrawingMatrix.load(getOrthoMatrix()); + outlineDrawingMatrix.multiply(*transform); + state->matrix.loadInverse(outlineDrawingMatrix); + + // compute area under rounded corners - only draws overlapping these rects need to be clipped + for (int i = 0 ; i < 4; i++) { + state->dangerRects[i] = bounds; + } + state->dangerRects[0].bottom = state->dangerRects[1].bottom = bounds.top + radius; + state->dangerRects[0].right = state->dangerRects[2].right = bounds.left + radius; + state->dangerRects[1].left = state->dangerRects[3].left = bounds.right - radius; + state->dangerRects[2].top = state->dangerRects[3].top = bounds.bottom - radius; + for (int i = 0; i < 4; i++) { + transform->mapRect(state->dangerRects[i]); + + // round danger rects out as though they are AA geometry (since they essentially are) + state->dangerRects[i].snapGeometryToPixelBoundaries(true); + } + + // store RR area + bounds.inset(radius); + state->outlineInnerRect = bounds; + state->outlineRadius = radius; + + // store as immutable so, for this frame, pointer uniquely identifies this bundle of shader info + roundRectClipState = state; +} + +/////////////////////////////////////////////////////////////////////////////// // Queries /////////////////////////////////////////////////////////////////////////////// @@ -215,7 +259,7 @@ bool Snapshot::isIgnored() const { void Snapshot::dump() const { ALOGD("Snapshot %p, flags %x, prev %p, height %d, ignored %d, hasComplexClip %d", - this, flags, previous.get(), height, isIgnored(), clipRegion && !clipRegion->isEmpty()); + this, flags, previous.get(), getViewportHeight(), isIgnored(), clipRegion && !clipRegion->isEmpty()); ALOGD(" ClipRect (at %p) %.1f %.1f %.1f %.1f", clipRect, clipRect->left, clipRect->top, clipRect->right, clipRect->bottom); ALOGD(" Transform (at %p):", transform); diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index 038aea8..435736c 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -20,6 +20,7 @@ #include <GLES2/gl2.h> #include <GLES2/gl2ext.h> +#include <utils/LinearAllocator.h> #include <utils/RefBase.h> #include <ui/Region.h> @@ -27,12 +28,40 @@ #include "Layer.h" #include "Matrix.h" +#include "Outline.h" #include "Rect.h" +#include "utils/Macros.h" namespace android { namespace uirenderer { /** + * Temporary structure holding information for a single outline clip. + * + * These structures are treated as immutable once created, and only exist for a single frame, which + * is why they may only be allocated with a LinearAllocator. + */ +class RoundRectClipState { +public: + /** static void* operator new(size_t size); PURPOSELY OMITTED, allocator only **/ + static void* operator new(size_t size, LinearAllocator& allocator) { + return allocator.alloc(size); + } + + bool areaRequiresRoundRectClip(const Rect& rect) const { + return rect.intersects(dangerRects[0]) + || rect.intersects(dangerRects[1]) + || rect.intersects(dangerRects[2]) + || rect.intersects(dangerRects[3]); + } + + Matrix4 matrix; + Rect dangerRects[4]; + Rect outlineInnerRect; + float outlineRadius; +}; + +/** * A snapshot holds information about the current state of the rendering * surface. A snapshot is usually created whenever the user calls save() * and discarded when the user calls restore(). Once a snapshot is created, @@ -65,17 +94,16 @@ public: * Indicates that this snapshot is a special type of layer * backed by an FBO. This flag only makes sense when the * flag kFlagIsLayer is also set. + * + * Viewport has been modified to fit the new Fbo, and must be + * restored when this snapshot is restored. */ kFlagIsFboLayer = 0x4, /** - * Indicates that this snapshot has changed the ortho matrix. - */ - kFlagDirtyOrtho = 0x8, - /** * Indicates that this snapshot or an ancestor snapshot is * an FBO layer. */ - kFlagFboTarget = 0x10 + kFlagFboTarget = 0x8, }; /** @@ -125,6 +153,19 @@ public: */ void resetTransform(float x, float y, float z); + void initializeViewport(int width, int height) { + mViewportData.initialize(width, height); + } + + int getViewportWidth() const { return mViewportData.mWidth; } + int getViewportHeight() const { return mViewportData.mHeight; } + const Matrix4& getOrthoMatrix() const { return mViewportData.mOrthoMatrix; } + + /** + * Sets (and replaces) the current clipping outline + */ + void setClippingOutline(LinearAllocator& allocator, const Outline* outline); + /** * Indicates whether this snapshot should be ignored. A snapshot * is typicalled ignored if its layer is invisible or empty. @@ -173,21 +214,6 @@ public: bool empty; /** - * Current viewport. - */ - Rect viewport; - - /** - * Height of the framebuffer the snapshot is rendering into. - */ - int height; - - /** - * Contains the previous ortho matrix. - */ - mat4 orthoMatrix; - - /** * Local transformation. Holds the current translation, scale and * rotation values. * @@ -233,9 +259,38 @@ public: */ float alpha; + /** + * Current clipping round rect. + * + * Points to data not owned by the snapshot, and may only be replaced by subsequent RR clips, + * never modified. + */ + const RoundRectClipState* roundRectClipState; + void dump() const; private: + struct ViewportData { + ViewportData() : mWidth(0), mHeight() {} + void initialize(int width, int height) { + mWidth = width; + mHeight = height; + mOrthoMatrix.loadOrtho(0, width, height, 0, -1, 1); + } + + /* + * Width and height of current viewport. + * + * The viewport is always defined to be (0, 0, width, height). + */ + int mWidth; + int mHeight; + /** + * Contains the current orthographic, projection matrix. + */ + mat4 mOrthoMatrix; + }; + void ensureClipRegion(); void copyClipRectFromRegion(); @@ -246,6 +301,7 @@ private: Rect mLocalClip; // don't use directly, call getLocalClip() which initializes this SkRegion mClipRegionRoot; + ViewportData mViewportData; }; // class Snapshot diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp index 05f6cf8..7d299f0 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define LOG_TAG "OpenGLRenderer" + #include <SkCanvas.h> #include "StatefulBaseRenderer.h" @@ -38,9 +40,7 @@ void StatefulBaseRenderer::initializeSaveStack(float clipLeft, float clipTop, void StatefulBaseRenderer::initializeViewport(int width, int height) { mWidth = width; mHeight = height; - - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); + mFirstSnapshot->initializeViewport(width, height); } /////////////////////////////////////////////////////////////////////////////// @@ -182,6 +182,10 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { return !mSnapshot->clipRect->isEmpty(); } +void StatefulBaseRenderer::setClippingOutline(LinearAllocator& allocator, const Outline* outline) { + mSnapshot->setClippingOutline(allocator, outline); +} + /////////////////////////////////////////////////////////////////////////////// // Quick Rejection /////////////////////////////////////////////////////////////////////////////// @@ -197,7 +201,9 @@ bool StatefulBaseRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { * See Rect::snapGeometryToPixelBoundaries() */ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, - float right, float bottom, bool* clipRequired, bool snapOut) const { + float right, float bottom, + bool* clipRequired, bool* roundRectClipRequired, + bool snapOut) const { if (mSnapshot->isIgnored() || bottom <= top || right <= left) { return true; } @@ -212,7 +218,15 @@ bool StatefulBaseRenderer::calculateQuickRejectForScissor(float left, float top, if (!clipRect.intersects(r)) return true; // clip is required if geometry intersects clip rect - if (clipRequired) *clipRequired = !clipRect.contains(r); + if (clipRequired) { + *clipRequired = !clipRect.contains(r); + } + + // round rect clip is required if RR clip exists, and geometry intersects its corners + if (roundRectClipRequired) { + *roundRectClipRequired = mSnapshot->roundRectClipState != NULL + && mSnapshot->roundRectClipState->areaRequiresRoundRectClip(r); + } return false; } diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index 64354ac..2e7f279 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -46,6 +46,11 @@ public: virtual status_t prepare(bool opaque) { return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque); } + + /** + * Initialize the first snapshot, computing the projection matrix, + * and stores the dimensions of the render target. + */ void initializeViewport(int width, int height); void initializeSaveStack(float clipLeft, float clipTop, float clipRight, float clipBottom); @@ -83,6 +88,14 @@ public: virtual bool clipPath(const SkPath* path, SkRegion::Op op); virtual bool clipRegion(const SkRegion* region, SkRegion::Op op); + /** + * Does not support different clipping Ops (that is, every call to setClippingOutline is + * effectively using SkRegion::kReplaceOp) + * + * The clipping outline is independent from the regular clip. + */ + void setClippingOutline(LinearAllocator& allocator, const Outline* outline); + protected: const Rect& getRenderTargetClipBounds() const { return mSnapshot->getRenderTargetClip(); } @@ -101,7 +114,7 @@ protected: // Clip bool calculateQuickRejectForScissor(float left, float top, float right, float bottom, - bool* clipRequired, bool snapOut) const; + bool* clipRequired, bool* roundRectClipRequired, bool snapOut) const; /** * Called just after a restore has occurred. The 'removed' snapshot popped from the stack, diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index fc5994c..d4a23b8 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -21,12 +21,12 @@ namespace android { namespace uirenderer { -class BaseAnimator; +class BaseRenderNodeAnimator; class AnimationListener; class AnimationHook { public: - virtual void callOnFinished(BaseAnimator* animator, AnimationListener* listener) = 0; + virtual void callOnFinished(BaseRenderNodeAnimator* animator, AnimationListener* listener) = 0; protected: ~AnimationHook() {} }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 5754536..48cd8fc 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -31,11 +31,10 @@ #define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" #define GLES_VERSION 2 +#define USE_TEXTURE_ATLAS false -#ifdef USE_OPENGL_RENDERER // Android-specific addition that is used to show when frames began in systrace EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); -#endif namespace android { namespace uirenderer { @@ -230,7 +229,9 @@ void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, } void GlobalContext::initAtlas() { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + if (USE_TEXTURE_ATLAS) { + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + } } void GlobalContext::usePBufferSurface() { diff --git a/libs/hwui/utils/MathUtils.h b/libs/hwui/utils/MathUtils.h index 1a7082b..997acde2 100644 --- a/libs/hwui/utils/MathUtils.h +++ b/libs/hwui/utils/MathUtils.h @@ -34,6 +34,10 @@ public: return value >= gNonZeroEpsilon; } + inline static bool areEqual(float valueA, float valueB) { + return isZero(valueA - valueB); + } + inline static int min(int a, int b) { return a < b ? a : b; } |
