diff options
Diffstat (limited to 'libs/hwui')
61 files changed, 2000 insertions, 1087 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 900a72e..a704e19 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 \ @@ -44,6 +45,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) RenderBufferCache.cpp \ RenderNode.cpp \ RenderProperties.cpp \ + RenderState.cpp \ ResourceCache.cpp \ ShadowTessellator.cpp \ SkiaShader.cpp \ @@ -60,6 +62,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SRC_FILES += \ renderthread/CanvasContext.cpp \ renderthread/DrawFrameTask.cpp \ + renderthread/EglManager.cpp \ renderthread/RenderProxy.cpp \ renderthread/RenderTask.cpp \ renderthread/RenderThread.cpp \ @@ -68,12 +71,8 @@ ifeq ($(USE_OPENGL_RENDERER),true) intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) LOCAL_C_INCLUDES += \ - $(JNI_H_INCLUDE) \ - $(LOCAL_PATH)/../../include/utils \ external/skia/src/core - include external/stlport/libstlport.mk - LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_MODULE_CLASS := SHARED_LIBRARIES @@ -81,16 +80,15 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_MODULE := libhwui LOCAL_MODULE_TAGS := optional + include external/stlport/libstlport.mk + ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) LOCAL_CFLAGS += -DANDROID_ENABLE_RENDERSCRIPT - LOCAL_SHARED_LIBRARIES += libRS libRScpp libstlport + LOCAL_SHARED_LIBRARIES += libRS libRScpp LOCAL_C_INCLUDES += \ $(intermediates) \ frameworks/rs/cpp \ - frameworks/rs \ - external/stlport/stlport \ - bionic/ \ - bionic/libstdc++/include + frameworks/rs endif ifndef HWUI_COMPILE_SYMBOLS diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp index eff3011..dc6d852 100644 --- a/libs/hwui/Animator.cpp +++ b/libs/hwui/Animator.cpp @@ -160,6 +160,10 @@ void RenderPropertyAnimator::onAttached(RenderNode* target) { (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue()); } +uint32_t RenderPropertyAnimator::dirtyMask() { + return mPropertyAccess->dirtyMask; +} + float RenderPropertyAnimator::getValue(RenderNode* target) const { return (target->properties().*mPropertyAccess->getter)(); } diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h index a0c7c55..6cb72c4c 100644 --- a/libs/hwui/Animator.h +++ b/libs/hwui/Animator.h @@ -61,6 +61,8 @@ public: bool isFinished() { return mPlayState == FINISHED; } float finalValue() { return mFinalValue; } + ANDROID_API virtual uint32_t dirtyMask() { return 0; } + protected: BaseRenderNodeAnimator(float finalValue); virtual ~BaseRenderNodeAnimator(); @@ -112,12 +114,14 @@ public: ANDROID_API virtual void onAttached(RenderNode* target); + ANDROID_API virtual uint32_t dirtyMask(); + protected: virtual float getValue(RenderNode* target) const; virtual void setValue(RenderNode* target, float value); private: - typedef void (RenderProperties::*SetFloatProperty)(float value); + typedef bool (RenderProperties::*SetFloatProperty)(float value); typedef float (RenderProperties::*GetFloatProperty)() const; struct PropertyAccessors; diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 6fd9999..402f28b 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -565,11 +565,8 @@ void Caches::deleteTexture(GLuint texture) { // call, any texture operation will be performed on the default // texture (name=0) - for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) { - if (mBoundTextures[i] == texture) { - mBoundTextures[i] = 0; - } - } + unbindTexture(texture); + glDeleteTextures(1, &texture); } @@ -577,6 +574,14 @@ void Caches::resetBoundTextures() { memset(mBoundTextures, 0, REQUIRED_TEXTURE_UNITS_COUNT * sizeof(GLuint)); } +void Caches::unbindTexture(GLuint texture) { + for (int i = 0; i < REQUIRED_TEXTURE_UNITS_COUNT; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + /////////////////////////////////////////////////////////////////////////////// // Scissor /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index b4b5927..83a5d9a 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -261,6 +261,11 @@ public: void resetBoundTextures(); /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); + + /** * Sets the scissor for the current surface. */ bool setScissor(GLint x, GLint y, GLint width, GLint height); diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp new file mode 100644 index 0000000..8b32c40 --- /dev/null +++ b/libs/hwui/DamageAccumulator.cpp @@ -0,0 +1,219 @@ +/* + * 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 { + +NullDamageAccumulator NullDamageAccumulator::sInstance; + +NullDamageAccumulator* NullDamageAccumulator::instance() { + return &sInstance; +} + +enum TransformType { + TransformInvalid = 0, + TransformRenderNode, + TransformMatrix4, + TransformNone, +}; + +struct DirtyStack { + TransformType type; + union { + const RenderNode* renderNode; + const Matrix4* matrix4; + }; + // 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; + mHead->type = TransformNone; +} + +void DamageAccumulator::pushCommon() { + if (!mHead->next) { + DirtyStack* nextFrame = (DirtyStack*) mAllocator.alloc(sizeof(DirtyStack)); + nextFrame->next = 0; + nextFrame->prev = mHead; + mHead->next = nextFrame; + } + mHead = mHead->next; + mHead->pendingDirty.setEmpty(); +} + +void DamageAccumulator::pushTransform(const RenderNode* transform) { + pushCommon(); + mHead->type = TransformRenderNode; + mHead->renderNode = transform; +} + +void DamageAccumulator::pushTransform(const Matrix4* transform) { + pushCommon(); + mHead->type = TransformMatrix4; + mHead->matrix4 = transform; +} + +void DamageAccumulator::pushNullTransform() { + pushCommon(); + mHead->type = TransformNone; +} + +void DamageAccumulator::popTransform() { + LOG_ALWAYS_FATAL_IF(mHead->prev == mHead, "Cannot pop the root frame!"); + DirtyStack* dirtyFrame = mHead; + mHead = mHead->prev; + switch (dirtyFrame->type) { + case TransformRenderNode: + applyRenderNodeTransform(dirtyFrame); + break; + case TransformMatrix4: + applyMatrix4Transform(dirtyFrame); + break; + case TransformNone: + mHead->pendingDirty.join(dirtyFrame->pendingDirty); + break; + default: + LOG_ALWAYS_FATAL("Tried to pop an invalid type: %d", dirtyFrame->type); + } +} + +static inline void mapRect(const Matrix4* matrix, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + Rect temp(in); + matrix->mapRect(temp); + out->join(RECT_ARGS(temp)); +} + +void DamageAccumulator::applyMatrix4Transform(DirtyStack* frame) { + mapRect(frame->matrix4, frame->pendingDirty, &mHead->pendingDirty); +} + +static inline void mapRect(const RenderProperties& props, const SkRect& in, SkRect* out) { + if (in.isEmpty()) return; + const SkMatrix* transform = props.getTransformMatrix(); + SkRect temp(in); + if (transform && !transform->isIdentity()) { + transform->mapRect(&temp); + } + temp.offset(props.getLeft(), props.getTop()); + out->join(temp); +} + +static DirtyStack* findParentRenderNode(DirtyStack* frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode) { + return frame; + } + } + return NULL; +} + +static DirtyStack* findProjectionReceiver(DirtyStack* frame) { + if (frame) { + while (frame->prev != frame) { + frame = frame->prev; + if (frame->type == TransformRenderNode + && frame->renderNode->hasProjectionReceiver()) { + return frame; + } + } + } + return NULL; +} + +static void applyTransforms(DirtyStack* frame, DirtyStack* end) { + SkRect* rect = &frame->pendingDirty; + while (frame != end) { + if (frame->type == TransformRenderNode) { + mapRect(frame->renderNode->properties(), *rect, rect); + } else { + mapRect(frame->matrix4, *rect, rect); + } + frame = frame->prev; + } +} + +void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { + if (frame->pendingDirty.isEmpty()) { + return; + } + + const RenderProperties& props = frame->renderNode->properties(); + if (props.getAlpha() <= 0) { + return; + } + + // Perform clipping + if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) { + if (!frame->pendingDirty.intersect(0, 0, props.getWidth(), props.getHeight())) { + frame->pendingDirty.setEmpty(); + } + } + + // apply all transforms + mapRect(props, frame->pendingDirty, &mHead->pendingDirty); + + // project backwards if necessary + if (props.getProjectBackwards() && !frame->pendingDirty.isEmpty()) { + // First, find our parent RenderNode: + DirtyStack* parentNode = findParentRenderNode(frame); + // Find our parent's projection receiver, which is what we project onto + DirtyStack* projectionReceiver = findProjectionReceiver(parentNode); + if (projectionReceiver) { + applyTransforms(frame, projectionReceiver); + projectionReceiver->pendingDirty.join(frame->pendingDirty); + } + + frame->pendingDirty.setEmpty(); + } +} + +void DamageAccumulator::dirty(float left, float top, float right, float bottom) { + mHead->pendingDirty.join(left, top, right, bottom); +} + +void DamageAccumulator::peekAtDirty(SkRect* dest) { + *dest = mHead->pendingDirty; +} + +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..fc9b41b --- /dev/null +++ b/libs/hwui/DamageAccumulator.h @@ -0,0 +1,103 @@ +/* + * 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 <cutils/compiler.h> +#include <utils/LinearAllocator.h> + +#include <SkMatrix.h> +#include <SkRect.h> + +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +struct DirtyStack; +class RenderNode; +class Matrix4; + +class IDamageAccumulator { +public: + virtual void pushTransform(const RenderNode* transform) = 0; + virtual void pushTransform(const Matrix4* transform) = 0; + virtual void pushNullTransform() = 0; + virtual void popTransform() = 0; + virtual void dirty(float left, float top, float right, float bottom) = 0; + virtual void peekAtDirty(SkRect* dest) = 0; +protected: + virtual ~IDamageAccumulator() {} +}; + +class DamageAccumulator : public IDamageAccumulator { + 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 transform when popTransform() is called. + virtual void pushTransform(const RenderNode* transform); + virtual void pushTransform(const Matrix4* transform); + // This is used in combination with peekAtDirty to inspect the damage + // area of a subtree + virtual void pushNullTransform(); + + // Pops a transform node from the stack, propagating the dirty rect + // up to the parent node. Returns the IDamageTransform that was just applied + virtual void popTransform(); + + virtual void dirty(float left, float top, float right, float bottom); + + // Returns the current dirty area, *NOT* transformed by pushed transforms + virtual void peekAtDirty(SkRect* dest); + + void finish(SkRect* totalDirty); + +private: + void pushCommon(); + void applyMatrix4Transform(DirtyStack* frame); + void applyRenderNodeTransform(DirtyStack* frame); + + LinearAllocator mAllocator; + DirtyStack* mHead; +}; + +class NullDamageAccumulator : public IDamageAccumulator { + PREVENT_COPY_AND_ASSIGN(NullDamageAccumulator); +public: + virtual void pushTransform(const RenderNode* transform) { } + virtual void pushTransform(const Matrix4* transform) { } + virtual void pushNullTransform() { } + virtual void popTransform() { } + virtual void dirty(float left, float top, float right, float bottom) { } + virtual void peekAtDirty(SkRect* dest) { dest->setEmpty(); } + + ANDROID_API static NullDamageAccumulator* instance(); + +private: + NullDamageAccumulator() {} + ~NullDamageAccumulator() {} + + static NullDamageAccumulator sInstance; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* DAMAGEACCUMULATOR_H */ diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 937bf8d..a998594 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -500,7 +500,7 @@ void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* o void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) { /* 1: op calculates local bounds */ DeferredDisplayState* const state = createState(); - if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) { + if (op->getLocalBounds(state->mBounds)) { if (state->mBounds.isEmpty()) { // valid empty bounds, don't bother deferring tryRecycleState(state); diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h index 48489c2..8a015b2 100644 --- a/libs/hwui/DeferredDisplayList.h +++ b/libs/hwui/DeferredDisplayList.h @@ -38,7 +38,6 @@ class SaveLayerOp; class StateOp; class DeferredDisplayState; -class OpenGLRenderer; class Batch; class DrawBatch; diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index 97e9bf6..8e99b9a 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -27,8 +27,7 @@ static void defaultLayerDestroyer(Layer* layer) { } DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroyer) - : mDisplayList(0) - , mSurfaceTexture(0) + : mSurfaceTexture(0) , mTransform(0) , mNeedsGLContextAttach(false) , mUpdateTexImage(false) @@ -41,7 +40,6 @@ DeferredLayerUpdater::DeferredLayerUpdater(Layer* layer, LayerDestroyer destroye mColorFilter = SkSafeRef(mLayer->getColorFilter()); mAlpha = mLayer->getAlpha(); mMode = mLayer->getMode(); - mDirtyRect.setEmpty(); if (!mDestroyer) { mDestroyer = defaultLayerDestroyer; @@ -60,33 +58,13 @@ void DeferredLayerUpdater::setPaint(const SkPaint* paint) { SkRefCnt_SafeAssign(mColorFilter, colorFilter); } -void DeferredLayerUpdater::setDisplayList(RenderNode* displayList, - int left, int top, int right, int bottom) { - mDisplayList = displayList; - if (mDirtyRect.isEmpty()) { - mDirtyRect.set(left, top, right, bottom); - } else { - mDirtyRect.unionWith(Rect(left, top, right, bottom)); - } -} - bool DeferredLayerUpdater::apply(TreeInfo& info) { bool success = true; // These properties are applied the same to both layer types mLayer->setColorFilter(mColorFilter); mLayer->setAlpha(mAlpha, mMode); - if (mDisplayList.get()) { - if (mWidth != mLayer->layer.getWidth() || mHeight != mLayer->layer.getHeight()) { - success = LayerRenderer::resizeLayer(mLayer, mWidth, mHeight); - } - mLayer->setBlend(mBlend); - mDisplayList->prepareTree(info); - mLayer->updateDeferred(mDisplayList.get(), - mDirtyRect.left, mDirtyRect.top, mDirtyRect.right, mDirtyRect.bottom); - mDirtyRect.setEmpty(); - mDisplayList = 0; - } else if (mSurfaceTexture.get()) { + if (mSurfaceTexture.get()) { if (mNeedsGLContextAttach) { mNeedsGLContextAttach = false; mSurfaceTexture->attachToContext(mLayer->getTexture()); diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index b7cfe80..c76bd5e 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -23,7 +23,6 @@ #include <utils/StrongPointer.h> #include "Layer.h" -#include "OpenGLRenderer.h" #include "Rect.h" #include "RenderNode.h" @@ -74,9 +73,6 @@ public: mTransform = matrix ? new SkMatrix(*matrix) : 0; } - ANDROID_API void setDisplayList(RenderNode* displayList, - int left, int top, int right, int bottom); - ANDROID_API void setPaint(const SkPaint* paint); ANDROID_API bool apply(TreeInfo& info); @@ -94,11 +90,6 @@ private: int mAlpha; SkXfermode::Mode mMode; - // Layer type specific properties - // displayList and surfaceTexture are mutually exclusive, only 1 may be set - // dirtyRect is only valid if displayList is set - sp<RenderNode> mDisplayList; - Rect mDirtyRect; sp<GLConsumer> mSurfaceTexture; SkMatrix* mTransform; bool mNeedsGLContextAttach; diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 96c6292..e38b532 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -80,10 +80,6 @@ void DisplayListData::cleanupResources() { delete paths.itemAt(i); } - for (size_t i = 0; i < matrices.size(); i++) { - delete matrices.itemAt(i); - } - bitmapResources.clear(); ownedBitmapResources.clear(); patchResources.clear(); @@ -91,12 +87,11 @@ void DisplayListData::cleanupResources() { paints.clear(); regions.clear(); paths.clear(); - matrices.clear(); layers.clear(); } -void DisplayListData::addChild(DrawDisplayListOp* op) { - LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!"); +void DisplayListData::addChild(DrawRenderNodeOp* op) { + LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!"); mChildren.push(op); mReferenceHolders.push(op->renderNode()); diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 11e78b0..bfffbb4 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -61,7 +61,7 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; -class DrawDisplayListOp; +class DrawRenderNodeOp; /** * Holds data used in the playback a tree of DisplayLists. @@ -125,7 +125,6 @@ public: Vector<const SkPath*> paths; SortedVector<const SkPath*> sourcePaths; Vector<const SkRegion*> regions; - Vector<const SkMatrix*> matrices; Vector<Layer*> layers; uint32_t functorCount; bool hasDrawOps; @@ -134,8 +133,8 @@ public: return !displayListOps.size(); } - void addChild(DrawDisplayListOp* childOp); - const Vector<DrawDisplayListOp*>& children() { return mChildren; } + void addChild(DrawRenderNodeOp* childOp); + const Vector<DrawRenderNodeOp*>& children() { return mChildren; } void refProperty(CanvasPropertyPrimitive* prop) { mReferenceHolders.push(prop); @@ -149,7 +148,7 @@ private: Vector< sp<VirtualLightRefBase> > mReferenceHolders; // list of children display lists for quick, non-drawing traversal - Vector<DrawDisplayListOp*> mChildren; + Vector<DrawRenderNodeOp*> mChildren; void cleanupResources(); }; diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index 3281116..5ed04a0 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -171,7 +171,7 @@ public: * * returns true if bounds exist */ - virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + virtual bool getLocalBounds(Rect& localBounds) { return false; } @@ -253,7 +253,7 @@ public: // default empty constructor for bounds, to be overridden in child constructor body DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { } - bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + bool getLocalBounds(Rect& localBounds) { localBounds.set(mLocalBounds); OpenGLRenderer::TextShadow textShadow; if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) { @@ -472,7 +472,7 @@ private: class SetMatrixOp : public StateOp { public: - SetMatrixOp(const SkMatrix* matrix) + SetMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -480,22 +480,22 @@ public: } virtual void output(int level, uint32_t logFlags) const { - if (mMatrix) { - OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); - } else { + if (mMatrix.isIdentity()) { OP_LOGS("SetMatrix (reset)"); + } else { + OP_LOG("SetMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } } virtual const char* name() { return "SetMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ConcatMatrixOp : public StateOp { public: - ConcatMatrixOp(const SkMatrix* matrix) + ConcatMatrixOp(const SkMatrix& matrix) : mMatrix(matrix) {} virtual void applyState(OpenGLRenderer& renderer, int saveCount) const { @@ -503,13 +503,13 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("ConcatMatrix " SK_MATRIX_STRING, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "ConcatMatrix"; } private: - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class ClipOp : public StateOp { @@ -746,10 +746,10 @@ protected: class DrawBitmapMatrixOp : public DrawBoundedOp { public: - DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) + DrawBitmapMatrixOp(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) : DrawBoundedOp(paint), mBitmap(bitmap), mMatrix(matrix) { mLocalBounds.set(0, 0, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(mLocalBounds); } @@ -758,7 +758,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(mMatrix)); + OP_LOG("Draw bitmap %p matrix " SK_MATRIX_STRING, mBitmap, SK_MATRIX_ARGS(&mMatrix)); } virtual const char* name() { return "DrawBitmapMatrix"; } @@ -770,7 +770,7 @@ public: private: const SkBitmap* mBitmap; - const SkMatrix* mMatrix; + const SkMatrix mMatrix; }; class DrawBitmapRectOp : public DrawBoundedOp { @@ -788,7 +788,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p src="RECT_STRING", dst="RECT_STRING, + OP_LOG("Draw bitmap %p src=" RECT_STRING ", dst=" RECT_STRING, mBitmap, RECT_ARGS(mSrc), RECT_ARGS(mLocalBounds)); } @@ -978,7 +978,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw patch "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPatch"; } @@ -1029,7 +1029,7 @@ public: DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint) {}; - bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) { + bool getLocalBounds(Rect& localBounds) { localBounds.set(mLocalBounds); if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) { localBounds.outset(strokeWidthOutset()); @@ -1060,7 +1060,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Rect "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Rect " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, @@ -1111,15 +1111,15 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw RoundRect "RECT_STRING", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); + OP_LOG("Draw RoundRect " RECT_STRING ", rx %f, ry %f", RECT_ARGS(mLocalBounds), mRx, mRy); } virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { DrawStrokableOp::onDefer(renderer, deferInfo, state); if (!mPaint->getPathEffect()) { - renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, - mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint); + renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint, + mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy); } } @@ -1184,7 +1184,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Oval "RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Oval " RECT_STRING, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawOval"; } @@ -1204,7 +1204,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Arc "RECT_STRING", start %f, sweep %f, useCenter %d", + OP_LOG("Draw Arc " RECT_STRING ", start %f, sweep %f, useCenter %d", RECT_ARGS(mLocalBounds), mStartAngle, mSweepAngle, mUseCenter); } @@ -1241,7 +1241,7 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw Path %p in " RECT_STRING, mPath, RECT_ARGS(mLocalBounds)); } virtual const char* name() { return "DrawPath"; } @@ -1399,7 +1399,7 @@ public: virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { Rect bounds; - getLocalBounds(renderer.getDrawModifiers(), bounds); + getLocalBounds(bounds); return renderer.drawText(mText, mBytesCount, mCount, mX, mY, mPositions, getPaint(renderer), mTotalAdvance, bounds); } @@ -1465,48 +1465,48 @@ private: Functor* mFunctor; }; -class DrawDisplayListOp : public DrawBoundedOp { - friend class RenderNode; // grant DisplayList access to info of child +class DrawRenderNodeOp : public DrawBoundedOp { + friend class RenderNode; // grant RenderNode access to info of child public: - DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent) - : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0), - mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {} + DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent) + : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0), + mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {} virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { - mDisplayList->deferNodeInParent(deferStruct, level + 1); + if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) { + mRenderNode->defer(deferStruct, level + 1); } } virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level, bool useQuickReject) { - if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) { - mDisplayList->replayNodeInParent(replayStruct, level + 1); + if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) { + mRenderNode->replay(replayStruct, level + 1); } } - // NOT USED since replay() is overridden virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { - return DrawGlInfo::kStatusDone; + LOG_ALWAYS_FATAL("should not be called, because replay() is overridden"); + return 0; } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags); - if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) { - mDisplayList->output(level + 1); + OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags); + if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) { + mRenderNode->output(level + 1); } } - virtual const char* name() { return "DrawDisplayList"; } + virtual const char* name() { return "DrawRenderNode"; } - RenderNode* renderNode() { return mDisplayList; } + RenderNode* renderNode() { return mRenderNode; } private: - RenderNode* mDisplayList; + RenderNode* mRenderNode; const int mFlags; /////////////////////////// - // Properties below are used by DisplayList::computeOrderingImpl() and iterate() + // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations() /////////////////////////// /** * Records transform vs parent, used for computing total transform without rerunning DL contents @@ -1514,12 +1514,12 @@ private: const mat4 mTransformFromParent; /** - * Holds the transformation between the projection surface ViewGroup and this DisplayList + * Holds the transformation between the projection surface ViewGroup and this RenderNode * drawing instance. Represents any translations / transformations done within the drawing of * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this * DisplayList draw instance. * - * Note: doesn't include any transformation recorded within the DisplayList and its properties. + * Note: doesn't include transformation within the RenderNode, or its properties. */ mat4 mTransformFromCompositingAncestor; bool mSkipInOrderDraw; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 229afdf..7789358 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -141,14 +141,12 @@ void DisplayListRenderer::skew(float sx, float sy) { StatefulBaseRenderer::skew(sx, sy); } -void DisplayListRenderer::setMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::setMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) SetMatrixOp(matrix)); StatefulBaseRenderer::setMatrix(matrix); } -void DisplayListRenderer::concatMatrix(const SkMatrix* matrix) { - matrix = refMatrix(matrix); +void DisplayListRenderer::concatMatrix(const SkMatrix& matrix) { addStateOp(new (alloc()) ConcatMatrixOp(matrix)); StatefulBaseRenderer::concatMatrix(matrix); } @@ -171,18 +169,16 @@ bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { return StatefulBaseRenderer::clipRegion(region, op); } -status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList, - Rect& dirty, int32_t flags) { +status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) { // dirty is an out parameter and should not be recorded, // it matters only when replaying the display list - if (displayList->stagingProperties().isProjectionReceiver()) { + if (renderNode->stagingProperties().isProjectionReceiver()) { // use staging property, since recording on UI thread mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size(); } - DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList, - flags, *currentTransform()); + DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform()); addDrawOp(op); mDisplayListData->addChild(op); return DrawGlInfo::kStatusDone; @@ -203,10 +199,9 @@ status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, float left, flo return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t DisplayListRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { bitmap = refBitmap(bitmap); - matrix = refMatrix(matrix); paint = refPaint(paint); addDrawOp(new (alloc()) DrawBitmapMatrixOp(bitmap, matrix, paint)); @@ -311,6 +306,10 @@ status_t DisplayListRenderer::drawOval(float left, float top, float right, float status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom, float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) { + if (fabs(sweepAngle) > 360.0f) { + return drawOval(left, top, right, bottom, paint); + } + paint = refPaint(paint); addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint)); @@ -426,7 +425,7 @@ void DisplayListRenderer::addStateOp(StateOp* op) { void DisplayListRenderer::addDrawOp(DrawOp* op) { Rect localBounds; - if (op->getLocalBounds(mDrawModifiers, localBounds)) { + if (op->getLocalBounds(localBounds)) { bool rejected = quickRejectConservative(localBounds.left, localBounds.top, localBounds.right, localBounds.bottom); op->setQuickRejected(rejected); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index dff4f6c..8ca9af1 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -23,7 +23,6 @@ #include <cutils/compiler.h> #include "DisplayListLogBuffer.h" -#include "OpenGLRenderer.h" #include "RenderNode.h" namespace android { @@ -53,12 +52,12 @@ class StateOp; /** * Records drawing commands in a display list for later playback into an OpenGLRenderer. */ -class DisplayListRenderer: public OpenGLRenderer { +class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer { public: - ANDROID_API DisplayListRenderer(); + DisplayListRenderer(); virtual ~DisplayListRenderer(); - ANDROID_API DisplayListData* finishRecording(); + DisplayListData* finishRecording(); virtual bool isRecording() const { return true; } @@ -81,13 +80,13 @@ public: const SkPaint* paint, int flags); // Matrix - virtual void translate(float dx, float dy, float dz); + virtual void translate(float dx, float dy, float dz = 0.0f); virtual void rotate(float degrees); virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); - virtual void concatMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); + virtual void concatMatrix(const SkMatrix& matrix); // Clip virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); @@ -98,6 +97,10 @@ public: virtual void resetPaintFilter(); virtual void setupPaintFilter(int clearBits, int setBits); + bool isCurrentTransformSimple() { + return currentTransform()->isSimple(); + } + // ---------------------------------------------------------------------------- // Canvas draw operations // ---------------------------------------------------------------------------- @@ -106,7 +109,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -148,16 +151,10 @@ public: // Canvas draw operations - special // ---------------------------------------------------------------------------- virtual status_t drawLayer(Layer* layer, float x, float y); - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, - int32_t replayFlags); + virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags); // TODO: rename for consistency virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); -protected: - // NOTE: must override these to avoid calling into super class, which calls GL. These may be - // removed once DisplayListRenderer no longer inherits from OpenGLRenderer - virtual void onViewportInitialized() {}; - virtual void onSnapshotRestored() {}; private: void insertRestoreToCount(); @@ -238,17 +235,6 @@ private: return regionCopy; } - inline const SkMatrix* refMatrix(const SkMatrix* matrix) { - if (matrix) { - // Copying the matrix is cheap and prevents against the user changing - // the original matrix before the operation that uses it - const SkMatrix* copy = new SkMatrix(*matrix); - mDisplayListData->matrices.add(copy); - return copy; - } - return matrix; - } - inline Layer* refLayer(Layer* layer) { mDisplayListData->layers.add(layer); mCaches.resourceCache.incrementRefcount(layer); 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/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 4407ab0..bf0ab5c 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -592,7 +592,7 @@ void FontRenderer::setFont(const SkPaint* paint, const mat4& matrix) { } FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, const char *text, - uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) { + uint32_t startIndex, uint32_t len, int numGlyphs, float radius, const float* positions) { checkInit(); DropShadow image; @@ -613,8 +613,9 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co Rect bounds; mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions); - uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius; - uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius; + uint32_t intRadius = Blur::convertRadiusToInt(radius); + uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * intRadius; + uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * intRadius; uint32_t maxSize = Caches::getInstance().maxTextureSize; if (paddedWidth > maxSize || paddedHeight > maxSize) { @@ -635,8 +636,8 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(const SkPaint* paint, co memset(dataBuffer, 0, size); - int penX = radius - bounds.left; - int penY = radius - bounds.bottom; + int penX = intRadius - bounds.left; + int penY = intRadius - bounds.bottom; if ((bounds.right > bounds.left) && (bounds.top > bounds.bottom)) { // text has non-whitespace, so draw and blur to create the shadow @@ -727,9 +728,10 @@ void FontRenderer::removeFont(const Font* font) { } } -void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) { +void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, float radius) { + uint32_t intRadius = Blur::convertRadiusToInt(radius); #ifdef ANDROID_ENABLE_RENDERSCRIPT - if (width * height * radius >= RS_MIN_INPUT_CUTOFF) { + if (width * height * intRadius >= RS_MIN_INPUT_CUTOFF) { uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height); if (mRs == 0) { @@ -768,12 +770,12 @@ void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int } #endif - float *gaussian = new float[2 * radius + 1]; - Blur::generateGaussianWeights(gaussian, radius); + float *gaussian = new float[2 * intRadius + 1]; + Blur::generateGaussianWeights(gaussian, intRadius); uint8_t* scratch = new uint8_t[width * height]; - Blur::horizontal(gaussian, radius, *image, scratch, width, height); - Blur::vertical(gaussian, radius, scratch, *image, width, height); + Blur::horizontal(gaussian, intRadius, *image, scratch, width, height); + Blur::vertical(gaussian, intRadius, scratch, *image, width, height); delete[] gaussian; delete[] scratch; diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index 9259028..8ce22b0 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -129,7 +129,7 @@ public: // After renderDropShadow returns, the called owns the memory in DropShadow.image // and is responsible for releasing it when it's done with it DropShadow renderDropShadow(const SkPaint* paint, const char *text, uint32_t startIndex, - uint32_t len, int numGlyphs, uint32_t radius, const float* positions); + uint32_t len, int numGlyphs, float radius, const float* positions); void setTextureFiltering(bool linearFiltering) { mLinearFiltering = linearFiltering; @@ -218,7 +218,7 @@ private: int32_t width, int32_t height); // the input image handle may have its pointer replaced (to avoid copies) - void blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius); + void blurImage(uint8_t** image, int32_t width, int32_t height, float radius); }; }; // namespace uirenderer diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp index 1f84b86..fc0e8a0 100644 --- a/libs/hwui/Interpolator.cpp +++ b/libs/hwui/Interpolator.cpp @@ -112,6 +112,10 @@ float LUTInterpolator::interpolate(float input) { int i1 = (int) ipart; int i2 = MathUtils::min(i1 + 1, mSize - 1); + LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!" + " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f", + i1, i2, input, lutpos, mSize, mValues, ipart, weight); + float v1 = mValues[i1]; float v2 = mValues[i2]; diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index 6a2ef2a..73a4da5 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -20,6 +20,7 @@ #include "Caches.h" #include "DeferredDisplayList.h" +#include "RenderState.h" #include "Layer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" @@ -28,8 +29,10 @@ namespace android { namespace uirenderer { -Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight): - caches(Caches::getInstance()), texture(caches) { +Layer::Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight) + : caches(Caches::getInstance()) + , renderState(renderState) + , texture(caches) { mesh = NULL; meshElementCount = 0; cacheable = true; @@ -41,7 +44,7 @@ Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight): colorFilter = NULL; deferredUpdateScheduled = false; renderer = NULL; - displayList = NULL; + renderNode = NULL; fbo = 0; stencil = NULL; debugDrawUpdate = false; @@ -72,7 +75,7 @@ uint32_t Layer::computeIdealHeight(uint32_t layerHeight) { void Layer::requireRenderer() { if (!renderer) { - renderer = new LayerRenderer(this); + renderer = new LayerRenderer(renderState, this); renderer->initProperties(); } } @@ -123,28 +126,26 @@ bool Layer::resize(const uint32_t width, const uint32_t height) { void Layer::removeFbo(bool flush) { if (stencil) { - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + GLuint previousFbo = renderState.getFramebuffer(); + renderState.bindFramebuffer(fbo); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); caches.renderBufferCache.put(stencil); stencil = NULL; } if (fbo) { - if (flush) LayerRenderer::flushLayer(this); + if (flush) LayerRenderer::flushLayer(renderState, this); // If put fails the cache will delete the FBO caches.fboCache.put(fbo); fbo = 0; } } -void Layer::updateDeferred(RenderNode* displayList, - int left, int top, int right, int bottom) { +void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) { requireRenderer(); - this->displayList = displayList; + this->renderNode = renderNode; const Rect r(left, top, right, bottom); dirtyRect.unionWith(r); deferredUpdateScheduled = true; @@ -185,6 +186,7 @@ void Layer::deleteTexture() { } void Layer::clearTexture() { + caches.unbindTexture(texture.id); texture.id = 0; } @@ -218,14 +220,14 @@ void Layer::defer() { renderer->setupFrameState(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - displayList->computeOrdering(); - displayList->deferNodeTree(deferredState); + renderNode->computeOrdering(); + renderNode->defer(deferredState, 0); deferredUpdateScheduled = false; } void Layer::cancelDefer() { - displayList = NULL; + renderNode = NULL; deferredUpdateScheduled = false; if (deferredList) { delete deferredList; @@ -245,7 +247,7 @@ void Layer::flush() { renderer->finish(); dirtyRect.setEmpty(); - displayList = NULL; + renderNode = NULL; } } @@ -254,14 +256,14 @@ void Layer::render() { renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); - renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); + renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren); renderer->finish(); dirtyRect.setEmpty(); deferredUpdateScheduled = false; - displayList = NULL; + renderNode = NULL; } }; // namespace uirenderer diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 49610d5..0bf05d0 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -43,6 +43,7 @@ namespace uirenderer { // Forward declarations class Caches; +class RenderState; class OpenGLRenderer; class RenderNode; class DeferredDisplayList; @@ -53,7 +54,7 @@ class DeferStateStruct; */ class Layer { public: - Layer(const uint32_t layerWidth, const uint32_t layerHeight); + Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight); ~Layer(); static uint32_t computeIdealWidth(uint32_t layerWidth); @@ -85,8 +86,7 @@ public: regionRect.translate(layer.left, layer.top); } - void updateDeferred(RenderNode* displayList, - int left, int top, int right, int bottom); + void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom); inline uint32_t getWidth() const { return texture.width; @@ -297,7 +297,7 @@ public: */ bool deferredUpdateScheduled; OpenGLRenderer* renderer; - sp<RenderNode> displayList; + sp<RenderNode> renderNode; Rect dirtyRect; bool debugDrawUpdate; bool hasDrawnSinceUpdate; @@ -307,6 +307,8 @@ private: Caches& caches; + RenderState& renderState; + /** * Name of the FBO used to render the layer. If the name is 0 * this layer is not backed by an FBO, but a simple texture. diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index 6be0146..13869aa 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -91,7 +91,7 @@ void LayerCache::clear() { mCache.clear(); } -Layer* LayerCache::get(const uint32_t width, const uint32_t height) { +Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) { Layer* layer = NULL; LayerEntry entry(width, height); @@ -108,7 +108,7 @@ Layer* LayerCache::get(const uint32_t width, const uint32_t height) { } else { LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight); - layer = new Layer(entry.mWidth, entry.mHeight); + layer = new Layer(renderState, entry.mWidth, entry.mHeight); layer->setBlend(true); layer->setEmpty(true); layer->setFbo(0); diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 221bfe0..1b0fc2c 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -24,6 +24,8 @@ namespace android { namespace uirenderer { +class RenderState; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -55,7 +57,7 @@ public: * @param width The desired width of the layer * @param height The desired height of the layer */ - Layer* get(const uint32_t width, const uint32_t height); + Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height); /** * Adds the layer to the cache. The layer will not be added if there is diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index df9aee5..873baf5 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -20,6 +20,7 @@ #include <private/hwui/DrawGlInfo.h> +#include "RenderState.h" #include "LayerCache.h" #include "LayerRenderer.h" #include "Matrix.h" @@ -33,7 +34,9 @@ namespace uirenderer { // Rendering /////////////////////////////////////////////////////////////////////////////// -LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) { +LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer) + : OpenGLRenderer(renderState) + , mLayer(layer) { } LayerRenderer::~LayerRenderer() { @@ -43,7 +46,7 @@ status_t LayerRenderer::prepareDirty(float left, float top, float right, float b bool opaque) { LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo()); - glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo()); + renderState().bindFramebuffer(mLayer->getFbo()); const float width = mLayer->layer.getWidth(); const float height = mLayer->layer.getHeight(); @@ -180,7 +183,7 @@ void LayerRenderer::generateMesh() { // Layers management /////////////////////////////////////////////////////////////////////////////// -Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { +Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) { LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); Caches& caches = Caches::getInstance(); @@ -191,7 +194,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { } caches.activeTexture(0); - Layer* layer = caches.layerCache.get(width, height); + Layer* layer = caches.layerCache.get(renderState, width, height); if (!layer) { ALOGW("Could not obtain a layer"); return NULL; @@ -221,10 +224,9 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { layer->setDirty(true); layer->region.clear(); - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + GLuint previousFbo = renderState.getFramebuffer(); - glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + renderState.bindFramebuffer(layer->getFbo()); layer->bindTexture(); // Initialize the texture if needed @@ -235,7 +237,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { // This should only happen if we run out of memory if (glGetError() != GL_NO_ERROR) { ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); caches.resourceCache.decrementRefcount(layer); return NULL; } @@ -244,7 +246,7 @@ Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) { glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, layer->getTexture(), 0); - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); return layer; } @@ -265,10 +267,10 @@ bool LayerRenderer::resizeLayer(Layer* layer, uint32_t width, uint32_t height) { return true; } -Layer* LayerRenderer::createTextureLayer() { +Layer* LayerRenderer::createTextureLayer(RenderState& renderState) { LAYER_RENDERER_LOGD("Creating new texture layer"); - Layer* layer = new Layer(0, 0); + Layer* layer = new Layer(renderState, 0, 0); layer->setCacheable(false); layer->setTextureLayer(true); layer->setEmpty(true); @@ -332,27 +334,32 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { } } -void LayerRenderer::flushLayer(Layer* layer) { +void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { #ifdef GL_EXT_discard_framebuffer + if (!layer) return; + GLuint fbo = layer->getFbo(); - if (layer && fbo) { + if (fbo) { // If possible, discard any enqueud operations on deferred // rendering architectures if (Extensions::getInstance().hasDiscardFramebuffer()) { - GLuint previousFbo; - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo); + GLuint previousFbo = renderState.getFramebuffer(); + if (fbo != previousFbo) { + renderState.bindFramebuffer(fbo); + } const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 }; glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments); - if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + if (fbo != previousFbo) { + renderState.bindFramebuffer(previousFbo); + } } } #endif } -bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { +bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) { Caches& caches = Caches::getInstance(); if (layer && bitmap->width() <= caches.maxTextureSize && bitmap->height() <= caches.maxTextureSize) { @@ -367,7 +374,8 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { GLuint texture; GLuint previousFbo; - GLuint previousViewport[4]; + GLsizei previousViewportWidth; + GLsizei previousViewportHeight; GLenum format; GLenum type; @@ -402,9 +410,9 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { layer->setAlpha(255, SkXfermode::kSrc_Mode); layer->setFbo(fbo); - glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); - glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport); - glBindFramebuffer(GL_FRAMEBUFFER, fbo); + previousFbo = renderState.getFramebuffer(); + renderState.getViewport(&previousViewportWidth, &previousViewportHeight); + renderState.bindFramebuffer(fbo); glGenTextures(1, &texture); if ((error = glGetError()) != GL_NO_ERROR) goto error; @@ -429,7 +437,7 @@ bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { if ((error = glGetError()) != GL_NO_ERROR) goto error; { - LayerRenderer renderer(layer); + LayerRenderer renderer(renderState, layer); renderer.setViewport(bitmap->width(), bitmap->height()); renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, bitmap->width(), bitmap->height(), !layer->isBlend()); @@ -469,13 +477,12 @@ error: } #endif - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + renderState.bindFramebuffer(previousFbo); layer->setAlpha(alpha, mode); layer->setFbo(previousLayerFbo); caches.deleteTexture(texture); caches.fboCache.put(fbo); - glViewport(previousViewport[0], previousViewport[1], - previousViewport[2], previousViewport[3]); + renderState.setViewport(previousViewportWidth, previousViewportHeight); return status; } diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index e79e7b8..9202e49 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -27,6 +27,8 @@ namespace android { namespace uirenderer { +class RenderState; + /////////////////////////////////////////////////////////////////////////////// // Defines /////////////////////////////////////////////////////////////////////////////// @@ -44,7 +46,7 @@ namespace uirenderer { class LayerRenderer: public OpenGLRenderer { public: - ANDROID_API LayerRenderer(Layer* layer); + LayerRenderer(RenderState& renderState, Layer* layer); virtual ~LayerRenderer(); virtual void onViewportInitialized(int width, int height) { /* do nothing */ } @@ -52,16 +54,16 @@ public: virtual status_t clear(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - ANDROID_API static Layer* createTextureLayer(); - ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height); - ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); - ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, + static Layer* createTextureLayer(RenderState& renderState); + static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height); + static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height); + static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform); - ANDROID_API static void destroyLayer(Layer* layer); + static void destroyLayer(Layer* layer); ANDROID_API static void destroyLayerDeferred(Layer* layer); - ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap); + static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap); - static void flushLayer(Layer* layer); + static void flushLayer(RenderState& renderState, Layer* layer); protected: virtual void ensureStencilBuffer(); diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp index 2268386..9f2014f 100644 --- a/libs/hwui/Matrix.cpp +++ b/libs/hwui/Matrix.cpp @@ -417,6 +417,8 @@ void Matrix4::mapPoint(float& x, float& y) const { } void Matrix4::mapRect(Rect& r) const { + if (isIdentity()) return; + if (isSimple()) { MUL_ADD_STORE(r.left, data[kScaleX], data[kTranslateX]); MUL_ADD_STORE(r.right, data[kScaleX], data[kTranslateX]); diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h index e33a001..1c5c578 100644 --- a/libs/hwui/Matrix.h +++ b/libs/hwui/Matrix.h @@ -147,6 +147,7 @@ public: data[kTranslateX] += x; data[kTranslateY] += y; data[kTranslateZ] += z; + mType |= kTypeUnknown; } else { // Doing a translation will only affect the translate bit of the type // Save the type diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 31f399a..ede89d7 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -35,6 +35,7 @@ #include "DeferredDisplayList.h" #include "DisplayListRenderer.h" #include "Fence.h" +#include "RenderState.h" #include "PathTessellator.h" #include "Properties.h" #include "ShadowTessellator.h" @@ -129,8 +130,10 @@ static inline T min(T a, T b) { // Constructors/destructor /////////////////////////////////////////////////////////////////////////////// -OpenGLRenderer::OpenGLRenderer(): - mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) { +OpenGLRenderer::OpenGLRenderer(RenderState& renderState) + : mCaches(Caches::getInstance()) + , mExtensions(Extensions::getInstance()) + , mRenderState(renderState) { // *set* draw modifiers to be 0 memset(&mDrawModifiers, 0, sizeof(mDrawModifiers)); mDrawModifiers.mOverrideLayerAlpha = 1.0f; @@ -187,7 +190,7 @@ status_t OpenGLRenderer::startFrame() { discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom); - glViewport(0, 0, getWidth(), getHeight()); + mRenderState.setViewport(getWidth(), getHeight()); // Functors break the tiling extension in pretty spectacular ways // This ensures we don't use tiling when a functor is going to be @@ -311,46 +314,9 @@ void OpenGLRenderer::finish() { mFrameStarted = false; } -void OpenGLRenderer::interrupt() { - if (mCaches.currentProgram) { - if (mCaches.currentProgram->isInUse()) { - mCaches.currentProgram->remove(); - mCaches.currentProgram = NULL; - } - } - mCaches.resetActiveTexture(); - mCaches.unbindMeshBuffer(); - mCaches.unbindIndicesBuffer(); - mCaches.resetVertexPointers(); - mCaches.disableTexCoordsVertexArray(); - debugOverdraw(false, false); -} - -void OpenGLRenderer::resume() { - const Snapshot* snapshot = currentSnapshot(); - glViewport(0, 0, getViewportWidth(), getViewportHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo); - debugOverdraw(true, false); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - - mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST); - mCaches.enableScissor(); - mCaches.resetScissor(); - dirtyClip(); - - mCaches.activeTexture(0); - mCaches.resetBoundTextures(); - - mCaches.blend = true; - glEnable(GL_BLEND); - glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); - glBlendEquation(GL_FUNC_ADD); -} - void OpenGLRenderer::resumeAfterLayer() { - glViewport(0, 0, getViewportWidth(), getViewportHeight()); - glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo); + mRenderState.setViewport(getViewportWidth(), getViewportHeight()); + mRenderState.bindFramebuffer(currentSnapshot()->fbo); debugOverdraw(true, false); mCaches.resetScissor(); @@ -379,20 +345,19 @@ status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) { info.height = getViewportHeight(); currentTransform()->copyTo(&info.transform[0]); - bool dirtyClip = mDirtyClip; + bool prevDirtyClip = mDirtyClip; // setup GL state for functor if (mDirtyClip) { setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt() } - if (mCaches.enableScissor() || dirtyClip) { + if (mCaches.enableScissor() || prevDirtyClip) { setScissorFromClip(); } - interrupt(); - // call functor immediately after GL state setup - (*functor)(DrawGlInfo::kModeDraw, &info); + mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info); + // Scissor may have been modified, reset dirty clip + dirtyClip(); - resume(); return DrawGlInfo::kStatusDrew; } @@ -413,17 +378,7 @@ void OpenGLRenderer::endMark() const { } void OpenGLRenderer::debugOverdraw(bool enable, bool clear) { - if (mCaches.debugOverdraw && getTargetFbo() == 0) { - if (clear) { - mCaches.disableScissor(); - mCaches.stencil.clear(); - } - if (enable) { - mCaches.stencil.enableDebugWrite(); - } else { - mCaches.stencil.disable(); - } - } + mRenderState.debugOverdraw(enable, clear); } void OpenGLRenderer::renderOverdraw() { @@ -474,8 +429,8 @@ void OpenGLRenderer::countOverdraw() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { - if (layer->deferredUpdateScheduled && layer->renderer && - layer->displayList.get() && layer->displayList->isRenderable()) { + if (layer->deferredUpdateScheduled && layer->renderer + && layer->renderNode.get() && layer->renderNode->isRenderable()) { ATRACE_CALL(); Rect& dirty = layer->dirtyRect; @@ -528,7 +483,7 @@ void OpenGLRenderer::updateLayers() { if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { mLayerUpdates.clear(); - glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + mRenderState.bindFramebuffer(getTargetFbo()); } endMark(); } @@ -556,7 +511,7 @@ void OpenGLRenderer::flushLayers() { } mLayerUpdates.clear(); - glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo()); + mRenderState.bindFramebuffer(getTargetFbo()); endMark(); } @@ -620,7 +575,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer; if (restoreViewport) { - glViewport(0, 0, getViewportWidth(), getViewportHeight()); + mRenderState.setViewport(getViewportWidth(), getViewportHeight()); } if (restoreClip) { @@ -791,7 +746,7 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto } mCaches.activeTexture(0); - Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight()); + Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight()); if (!layer) { return false; } @@ -853,7 +808,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { endTiling(); debugOverdraw(false, false); // Bind texture to FBO - glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo()); + mRenderState.bindFramebuffer(layer->getFbo()); layer->bindTexture(); // Initialize the texture if needed @@ -876,7 +831,7 @@ bool OpenGLRenderer::createFboLayer(Layer* layer, Rect& bounds, Rect& clip) { dirtyClip(); // Change the ortho projection - glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); + mRenderState.setViewport(bounds.getWidth(), bounds.getHeight()); return true; } @@ -907,7 +862,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto layer->removeFbo(false); // Unbind current FBO and restore previous one - glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo); + mRenderState.bindFramebuffer(restored.fbo); debugOverdraw(true, false); startTilingCurrentClip(); @@ -1931,25 +1886,24 @@ void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) { // Drawing /////////////////////////////////////////////////////////////////////////////// -status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty, - int32_t replayFlags) { +status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) { status_t status; // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself - if (displayList && displayList->isRenderable()) { + if (renderNode && renderNode->isRenderable()) { // compute 3d ordering - displayList->computeOrdering(); + renderNode->computeOrdering(); if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { status = startFrame(); ReplayStateStruct replayStruct(*this, dirty, replayFlags); - displayList->replayNodeTree(replayStruct); + renderNode->replay(replayStruct, 0); return status | replayStruct.mDrawGlStatus; } bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); - displayList->deferNodeTree(deferStruct); + renderNode->defer(deferStruct, 0); flushLayers(); status = startFrame(); @@ -2043,10 +1997,10 @@ status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, float left, float to return DrawGlInfo::kStatusDrew; } -status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, +status_t OpenGLRenderer::drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) { Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); - const mat4 transform(*matrix); + const mat4 transform(matrix); transform.mapRect(r); if (quickRejectSetupScissor(r.left, r.top, r.right, r.bottom)) { @@ -2556,8 +2510,8 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float return drawShape(left, top, texture, p); } - const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(), - right - left, bottom - top, rx, ry, p); + const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect( + *currentTransform(), *p, right - left, bottom - top, rx, ry); return drawVertexBuffer(left, top, *vertexBuffer, p); } @@ -2611,10 +2565,6 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto return DrawGlInfo::kStatusDone; } - if (fabs(sweepAngle) >= 360.0f) { - return drawOval(left, top, right, bottom, p); - } - // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) { mCaches.activeTexture(0); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index b9b369f..4ff5780 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -57,6 +57,7 @@ namespace android { namespace uirenderer { class DeferredDisplayState; +class RenderState; class RenderNode; class TextSetupFunctor; class VertexBuffer; @@ -119,33 +120,31 @@ enum ModelViewMode { */ class OpenGLRenderer : public StatefulBaseRenderer { public: - ANDROID_API OpenGLRenderer(); + OpenGLRenderer(RenderState& renderState); virtual ~OpenGLRenderer(); - ANDROID_API void initProperties(); + void initProperties(); virtual void onViewportInitialized(); virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - virtual void interrupt(); - virtual void resume(); - ANDROID_API void setCountOverdrawEnabled(bool enabled) { + void setCountOverdrawEnabled(bool enabled) { mCountOverdraw = enabled; } - ANDROID_API float getOverdraw() { + float getOverdraw() { return mCountOverdraw ? mOverdraw : 0.0f; } virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); - ANDROID_API void pushLayerUpdate(Layer* layer); - ANDROID_API void cancelLayerUpdate(Layer* layer); - ANDROID_API void clearLayerUpdates(); - ANDROID_API void flushLayerUpdates(); + void pushLayerUpdate(Layer* layer); + void cancelLayerUpdate(Layer* layer); + void clearLayerUpdates(); + void flushLayerUpdates(); - ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom, + virtual int saveLayer(float left, float top, float right, float bottom, const SkPaint* paint, int flags) { return saveLayer(left, top, right, bottom, paint, flags, NULL); } @@ -158,13 +157,13 @@ public: int saveLayerDeferred(float left, float top, float right, float bottom, const SkPaint* paint, int flags); - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); + virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1); virtual status_t drawLayer(Layer* layer, float x, float y); virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint); status_t drawBitmaps(const SkBitmap* bitmap, AssetAtlas::Entry* entry, int bitmapCount, TextureVertex* vertices, bool pureTranslate, const Rect& bounds, const SkPaint* paint); - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint); virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -231,7 +230,7 @@ public: const DrawModifiers& getDrawModifiers() { return mDrawModifiers; } void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; } - ANDROID_API bool isCurrentTransformSimple() { + bool isCurrentTransformSimple() { return currentTransform()->isSimple(); } @@ -474,6 +473,8 @@ protected: return false; } + inline RenderState& renderState() { return mRenderState; } + private: /** * Discards the content of the framebuffer if supported by the driver. @@ -977,6 +978,7 @@ private: // Various caches Caches& mCaches; Extensions& mExtensions; + RenderState& mRenderState; // List of rectangles to clear after saveLayer() is invoked Vector<Rect*> mLayers; @@ -1012,7 +1014,6 @@ private: bool mSkipOutlineClip; - friend class DisplayListRenderer; friend class Layer; friend class TextSetupFunctor; friend class DrawBitmapOp; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index ab6b742..97eb583 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -104,8 +104,7 @@ void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, } static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { - bitmap.setConfig(SkBitmap::kA8_Config, width, height); - bitmap.allocPixels(); + bitmap.allocPixels(SkImageInfo::MakeA8(width, height)); bitmap.eraseColor(0); } @@ -339,7 +338,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { float left, top, offset; uint32_t width, height; - PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height); + PathCache::computePathBounds(t->path, &t->paint, left, top, offset, width, height); PathTexture* texture = t->texture; texture->left = left; @@ -350,7 +349,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); - drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + drawPath(t->path, &t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { texture->width = 0; diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 6177ff1..eee138b 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -272,7 +272,7 @@ private: class PathTask: public Task<SkBitmap*> { public: PathTask(const SkPath* path, const SkPaint* paint, PathTexture* texture): - path(path), paint(paint), texture(texture) { + path(path), paint(*paint), texture(texture) { } ~PathTask() { @@ -280,7 +280,8 @@ private: } const SkPath* path; - const SkPaint* paint; + //copied, since input paint may not be immutable + const SkPaint paint; PathTexture* texture; }; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index a58e3e7..310b107 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -57,17 +57,26 @@ namespace uirenderer { #define ROUND_CAP_THRESH 0.25f #define PI 3.1415926535897932f +// temporary error thresholds +#define ERROR_DEPTH 20 +#define ERROR_SCALE 1e10 +#define ERROR_SQR_INV_THRESH 1e-20 + void PathTessellator::extractTessellationScales(const Matrix4& transform, float* scaleX, float* scaleY) { - *scaleX = 1.0f; - *scaleY = 1.0f; - if (CC_UNLIKELY(!transform.isPureTranslate())) { + if (CC_LIKELY(transform.isPureTranslate())) { + *scaleX = 1.0f; + *scaleY = 1.0f; + } else { float m00 = transform.data[Matrix4::kScaleX]; float m01 = transform.data[Matrix4::kSkewY]; float m10 = transform.data[Matrix4::kSkewX]; float m11 = transform.data[Matrix4::kScaleY]; *scaleX = sqrt(m00 * m00 + m01 * m01); *scaleY = sqrt(m10 * m10 + m11 * m11); + + LOG_ALWAYS_FATAL_IF(*scaleX > ERROR_SCALE || *scaleY > ERROR_SCALE, + "scales %e x %e too large for tessellation", *scaleX, *scaleY); } } @@ -92,10 +101,12 @@ struct PaintInfo { public: PaintInfo(const SkPaint* paint, const mat4& transform) : style(paint->getStyle()), cap(paint->getStrokeCap()), isAA(paint->isAntiAlias()), - inverseScaleX(1.0f), inverseScaleY(1.0f), halfStrokeWidth(paint->getStrokeWidth() * 0.5f), maxAlpha(1.0f) { // compute inverse scales - if (CC_UNLIKELY(!transform.isPureTranslate())) { + if (CC_LIKELY(transform.isPureTranslate())) { + inverseScaleX = 1.0f; + inverseScaleY = 1.0f; + } else { float scaleX, scaleY; PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); inverseScaleX = (scaleX != 0) ? (1.0f / scaleX) : 1.0f; @@ -922,6 +933,9 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo Vector<Vertex>& outputVertices) { ATRACE_CALL(); + LOG_ALWAYS_FATAL_IF(sqrInvScaleX < ERROR_SQR_INV_THRESH || sqrInvScaleY < ERROR_SQR_INV_THRESH, + "Invalid scale factors used for approx %e, %e", sqrInvScaleX, sqrInvScaleY); + // TODO: to support joins other than sharp miter, join vertices should be labelled in the // perimeter, or resolved into more vertices. Reconsider forceClose-ing in that case. SkPath::Iter iter(path, forceClose); @@ -975,14 +989,14 @@ bool PathTessellator::approximatePathOutlineVertices(const SkPath& path, bool fo // Bezier approximation /////////////////////////////////////////////////////////////////////////////// -// Depth at which recursion is aborted -#define ABORT_DEPTH 10 - void PathTessellator::recursiveCubicBezierVertices( float p1x, float p1y, float c1x, float c1y, float p2x, float p2y, float c2x, float c2y, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR DEPTH exceeded: cubic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = p2x - p1x; float dy = p2y - p1y; float d1 = fabs((c1x - p2x) * dy - (c1y - p2y) * dx); @@ -990,8 +1004,7 @@ void PathTessellator::recursiveCubicBezierVertices( float d = d1 + d2; // multiplying by sqrInvScaleY/X equivalent to multiplying in dimensional scale factors - - if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, p2x, p2y); } else { @@ -1029,11 +1042,14 @@ void PathTessellator::recursiveQuadraticBezierVertices( float cx, float cy, float sqrInvScaleX, float sqrInvScaleY, float thresholdSquared, Vector<Vertex>& outputVertices, int depth) { + LOG_ALWAYS_FATAL_IF(depth >= ERROR_DEPTH, "ERROR_DEPTH exceeded: quadratic approx, invscale %e x %e, vertcount %d", + sqrInvScaleX, sqrInvScaleY, outputVertices.size()); + float dx = bx - ax; float dy = by - ay; float d = (cx - bx) * dy - (cy - by) * dx; - if (depth >= ABORT_DEPTH || d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { + if (d * d < thresholdSquared * (dx * dx * sqrInvScaleY + dy * dy * sqrInvScaleX)) { // below thresh, draw line by adding endpoint pushToVector(outputVertices, bx, by); } else { diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d964efc..131384a 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -15,19 +15,24 @@ */ #define ATRACE_TAG ATRACE_TAG_VIEW +#define LOG_TAG "OpenGLRenderer" #include "RenderNode.h" #include <algorithm> +#include <string> #include <SkCanvas.h> #include <algorithm> #include <utils/Trace.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "DisplayListOp.h" #include "DisplayListLogBuffer.h" +#include "LayerRenderer.h" +#include "OpenGLRenderer.h" #include "utils/MathUtils.h" namespace android { @@ -57,12 +62,14 @@ RenderNode::RenderNode() , mNeedsDisplayListDataSync(false) , mDisplayListData(0) , mStagingDisplayListData(0) - , mNeedsAnimatorsSync(false) { + , mNeedsAnimatorsSync(false) + , mLayer(0) { } RenderNode::~RenderNode() { delete mDisplayListData; delete mStagingDisplayListData; + LayerRenderer::destroyLayerDeferred(mLayer); } void RenderNode::setStagingDisplayList(DisplayListData* data) { @@ -110,14 +117,97 @@ void RenderNode::prepareTree(TreeInfo& info) { prepareTreeImpl(info); } -void RenderNode::prepareTreeImpl(TreeInfo& info) { - if (info.performStagingPush) { - pushStagingChanges(info); +void RenderNode::damageSelf(TreeInfo& info) { + if (isRenderable()) { + if (properties().getClipDamageToBounds()) { + info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight()); + } else { + // Hope this is big enough? + // TODO: Get this from the display list ops or something + info.damageAccumulator->dirty(INT_MIN, INT_MIN, INT_MAX, INT_MAX); + } + } +} + +void RenderNode::prepareLayer(TreeInfo& info) { + LayerType layerType = properties().layerProperties().type(); + if (CC_UNLIKELY(layerType == kLayerTypeRenderLayer)) { + // We push a null transform here as we don't care what the existing dirty + // area is, only what our display list dirty is as well as our children's + // dirty area + info.damageAccumulator->pushNullTransform(); + } +} + +void RenderNode::pushLayerUpdate(TreeInfo& info) { + LayerType layerType = properties().layerProperties().type(); + // If we are not a layer OR we cannot be rendered (eg, view was detached) + // we need to destroy any Layers we may have had previously + if (CC_LIKELY(layerType != kLayerTypeRenderLayer) || CC_UNLIKELY(!isRenderable())) { + if (layerType == kLayerTypeRenderLayer) { + info.damageAccumulator->popTransform(); + } + if (CC_UNLIKELY(mLayer)) { + LayerRenderer::destroyLayer(mLayer); + mLayer = NULL; + } + return; + } + + if (!mLayer) { + mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight()); + applyLayerPropertiesToLayer(info); + damageSelf(info); + } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) { + if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) { + LayerRenderer::destroyLayer(mLayer); + mLayer = 0; + } + damageSelf(info); + } + + SkRect dirty; + info.damageAccumulator->peekAtDirty(&dirty); + info.damageAccumulator->popTransform(); + + if (!mLayer) { + if (info.errorHandler) { + std::string msg = "Unable to create layer for "; + msg += getName(); + info.errorHandler->onError(msg); + } + return; } - if (info.evaluateAnimations) { + + if (!dirty.isEmpty()) { + mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom); + } + // This is not inside the above if because we may have called + // updateDeferred on a previous prepare pass that didn't have a renderer + if (info.renderer && mLayer->deferredUpdateScheduled) { + info.renderer->pushLayerUpdate(mLayer); + } +} + +void RenderNode::prepareTreeImpl(TreeInfo& info) { + info.damageAccumulator->pushTransform(this); + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingPropertiesChanges(info); evaluateAnimations(info); + } else if (info.mode == TreeInfo::MODE_MAYBE_DETACHING) { + pushStagingPropertiesChanges(info); + } else if (info.mode == TreeInfo::MODE_RT_ONLY) { + evaluateAnimations(info); + } + + prepareLayer(info); + if (info.mode == TreeInfo::MODE_FULL) { + pushStagingDisplayListChanges(info); } prepareSubTree(info, mDisplayListData); + pushLayerUpdate(info); + + info.damageAccumulator->popTransform(); } class PushAnimatorsFunctor { @@ -134,7 +224,7 @@ private: TreeInfo& mInfo; }; -void RenderNode::pushStagingChanges(TreeInfo& info) { +void RenderNode::pushStagingPropertiesChanges(TreeInfo& info) { // Push the animators first so that setupStartValueIfNecessary() is called // before properties() is trampled by stagingProperties(), as they are // required by some animators. @@ -149,18 +239,41 @@ void RenderNode::pushStagingChanges(TreeInfo& info) { } if (mDirtyPropertyFields) { mDirtyPropertyFields = 0; + damageSelf(info); + info.damageAccumulator->popTransform(); mProperties = mStagingProperties; + applyLayerPropertiesToLayer(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. + info.damageAccumulator->pushTransform(this); + damageSelf(info); } +} + +void RenderNode::applyLayerPropertiesToLayer(TreeInfo& info) { + if (CC_LIKELY(!mLayer)) return; + + const LayerProperties& props = properties().layerProperties(); + mLayer->setAlpha(props.alpha(), props.xferMode()); + mLayer->setColorFilter(props.colorFilter()); + mLayer->setBlend(props.needsBlending()); +} + +void RenderNode::pushStagingDisplayListChanges(TreeInfo& 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, info.renderState); + 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 +293,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); + info.damageAccumulator->popTransform(); + 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(); + + info.damageAccumulator->pushTransform(this); + damageSelf(info); } void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { @@ -201,8 +323,11 @@ void RenderNode::prepareSubTree(TreeInfo& info, DisplayListData* subtree) { info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]); } for (size_t i = 0; i < subtree->children().size(); i++) { - RenderNode* childNode = subtree->children()[i]->mDisplayList; + DrawRenderNodeOp* op = subtree->children()[i]; + RenderNode* childNode = op->mRenderNode; + info.damageAccumulator->pushTransform(&op->mTransformFromParent); childNode->prepareTreeImpl(info); + info.damageAccumulator->popTransform(); } } } @@ -223,9 +348,9 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.translate(properties().getLeft(), properties().getTop()); } if (properties().getStaticMatrix()) { - renderer.concatMatrix(properties().getStaticMatrix()); + renderer.concatMatrix(*properties().getStaticMatrix()); } else if (properties().getAnimationMatrix()) { - renderer.concatMatrix(properties().getAnimationMatrix()); + renderer.concatMatrix(*properties().getAnimationMatrix()); } if (properties().hasTransformMatrix()) { if (properties().isTransformTranslateOnly()) { @@ -234,9 +359,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { renderer.concatMatrix(*properties().getTransformMatrix()); } } - bool clipToBoundsNeeded = properties().getCaching() ? false : properties().getClipToBounds(); + const bool isLayer = properties().layerProperties().type() != kLayerTypeNone; + bool clipToBoundsNeeded = isLayer ? false : properties().getClipToBounds(); if (properties().getAlpha() < 1) { - if (properties().getCaching()) { + if (isLayer) { renderer.setOverrideLayerAlpha(properties().getAlpha()); } else if (!properties().getHasOverlappingRendering()) { renderer.scaleAlpha(properties().getAlpha()); @@ -329,16 +455,16 @@ void RenderNode::computeOrdering() { // transform properties are applied correctly to top level children if (mDisplayListData == NULL) return; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - childOp->mDisplayList->computeOrderingImpl(childOp, + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + childOp->mRenderNode->computeOrderingImpl(childOp, properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity()); } } void RenderNode::computeOrderingImpl( - DrawDisplayListOp* opState, + DrawRenderNodeOp* opState, const SkPath* outlineOfProjectionSurface, - Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface) { mProjectedNodes.clear(); if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return; @@ -362,11 +488,11 @@ void RenderNode::computeOrderingImpl( const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0; bool haveAppliedPropertiesToProjection = false; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mRenderNode; const SkPath* projectionOutline = NULL; - Vector<DrawDisplayListOp*>* projectionChildren = NULL; + Vector<DrawRenderNodeOp*>* projectionChildren = NULL; const mat4* projectionTransform = NULL; if (isProjectionReceiver && !child->properties().getProjectBackwards()) { // if receiving projections, collect projecting descendent @@ -410,15 +536,7 @@ private: const int mLevel; }; -void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) { - DeferOperationHandler handler(deferStruct, 0); - if (MathUtils::isPositive(properties().getZ())) { - issueDrawShadowOperation(Matrix4::identity(), handler); - } - issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); -} - -void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) { +void RenderNode::defer(DeferStateStruct& deferStruct, const int level) { DeferOperationHandler handler(deferStruct, level); issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler); } @@ -448,29 +566,21 @@ private: const int mLevel; }; -void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) { - ReplayOperationHandler handler(replayStruct, 0); - if (MathUtils::isPositive(properties().getZ())) { - issueDrawShadowOperation(Matrix4::identity(), handler); - } - issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); -} - -void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) { +void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) { ReplayOperationHandler handler(replayStruct, level); issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler); } -void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) { +void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) { if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return; for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) { - DrawDisplayListOp* childOp = mDisplayListData->children()[i]; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = mDisplayListData->children()[i]; + RenderNode* child = childOp->mRenderNode; float childZ = child->properties().getZ(); if (!MathUtils::isZero(childZ)) { - zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp)); + zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp)); childOp->mSkipInOrderDraw = true; } else if (!child->properties().getProjectBackwards()) { // regular, in order drawing DisplayList @@ -515,10 +625,40 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds()); } +template <class T> +int RenderNode::issueOperationsOfNegZChildren( + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler) { + if (zTranslatedNodes.isEmpty()) return -1; + + // create a save around the body of the ViewGroup's draw method, so that + // matrix/clip methods don't affect composited children + int shadowSaveCount = renderer.getSaveCount(); + handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + + issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); + return shadowSaveCount; +} + +template <class T> +void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo, + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler) { + if (zTranslatedNodes.isEmpty()) return; + + LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to"); + handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo), + PROPERTY_SAVECOUNT, properties().getClipToBounds()); + renderer.setOverrideLayerAlpha(1.0f); + + issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); +} + #define SHADOW_DELTA 0.1f template <class T> -void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, +void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) { const int size = zTranslatedNodes.size(); if (size == 0 @@ -553,8 +693,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair float lastCasterZ = 0.0f; while (shadowIndex < endIndex || drawIndex < endIndex) { if (shadowIndex < endIndex) { - DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value; - RenderNode* caster = casterOp->mDisplayList; + DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value; + RenderNode* caster = casterOp->mRenderNode; const float casterZ = zTranslatedNodes[shadowIndex].key; // attempt to render the shadow if the caster about to be drawn is its caster, // OR if its caster's Z value is similar to the previous potential caster @@ -571,8 +711,8 @@ void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair // since it modifies the renderer's matrix int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); - DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value; - RenderNode* child = childOp->mDisplayList; + DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value; + RenderNode* child = childOp->mRenderNode; renderer.concatMatrix(childOp->mTransformFromParent); childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone @@ -588,8 +728,6 @@ template <class T> void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) { DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size()); const SkPath* projectionReceiverOutline = properties().getOutline().getPath(); - bool maskProjecteesWithPath = projectionReceiverOutline != NULL - && !projectionReceiverOutline->isRect(NULL); int restoreTo = renderer.getSaveCount(); // If the projection reciever has an outline, we mask each of the projected rendernodes to it @@ -610,7 +748,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& SaveLayerOp* op = new (alloc) SaveLayerOp( outlineBounds.left(), outlineBounds.top(), outlineBounds.right(), outlineBounds.bottom(), - 255, SkCanvas::kARGB_ClipLayer_SaveFlag); + 255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag); op->setMask(projectionReceiverOutline); handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds()); @@ -623,7 +761,7 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& // draw projected nodes for (size_t i = 0; i < mProjectedNodes.size(); i++) { - DrawDisplayListOp* childOp = mProjectedNodes[i]; + DrawRenderNodeOp* childOp = mProjectedNodes[i]; // matrix save, concat, and restore can be done safely without allocating operations int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag); @@ -651,8 +789,14 @@ void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& */ template <class T> void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { + const bool drawLayer = (mLayer && (&renderer != mLayer->renderer)); + // If we are updating the contents of mLayer, we don't want to apply any of + // the RenderNode's properties to this issueOperations pass. Those will all + // be applied when the layer is drawn, aka when this is true. + const bool useViewProperties = (!mLayer || drawLayer); + const int level = handler.level(); - if (mDisplayListData->isEmpty() || properties().getAlpha() <= 0) { + if (mDisplayListData->isEmpty() || (useViewProperties && properties().getAlpha() <= 0)) { DISPLAY_LIST_LOGD("%*sEmpty display list (%p, %s)", level * 2, "", this, getName()); return; } @@ -674,7 +818,9 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { DISPLAY_LIST_LOGD("%*sSave %d %d", (level + 1) * 2, "", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag, restoreTo); - setViewProperties<T>(renderer, handler); + if (useViewProperties) { + setViewProperties<T>(renderer, handler); + } bool quickRejected = properties().getClipToBounds() && renderer.quickRejectConservative(0, 0, properties().getWidth(), properties().getHeight()); @@ -683,31 +829,36 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { renderer.setClippingOutline(alloc, &(mProperties.getOutline())); } - Vector<ZDrawDisplayListOpPair> zTranslatedNodes; - buildZSortedChildList(zTranslatedNodes); + if (drawLayer) { + handler(new (alloc) DrawLayerOp(mLayer, 0, 0), + renderer.getSaveCount() - 1, properties().getClipToBounds()); + } else { + Vector<ZDrawRenderNodeOpPair> zTranslatedNodes; + buildZSortedChildList(zTranslatedNodes); - // for 3d root, draw children with negative z values - issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler); + // for 3d root, draw children with negative z values + int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler); - DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); - const int saveCountOffset = renderer.getSaveCount() - 1; - const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; - for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { - DisplayListOp *op = mDisplayListData->displayListOps[i]; + DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance(); + const int saveCountOffset = renderer.getSaveCount() - 1; + const int projectionReceiveIndex = mDisplayListData->projectionReceiveIndex; + for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) { + DisplayListOp *op = mDisplayListData->displayListOps[i]; #if DEBUG_DISPLAY_LIST - op->output(level + 1); + op->output(level + 1); #endif - logBuffer.writeCommand(level, op->name()); - handler(op, saveCountOffset, properties().getClipToBounds()); + logBuffer.writeCommand(level, op->name()); + handler(op, saveCountOffset, properties().getClipToBounds()); - if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { - issueOperationsOfProjectedChildren(renderer, handler); + if (CC_UNLIKELY(i == projectionReceiveIndex && mProjectedNodes.size() > 0)) { + issueOperationsOfProjectedChildren(renderer, handler); + } } - } - // for 3d root, draw children with positive z values - issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler); + // for 3d root, draw children with positive z values + issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler); + } } DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo); diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 1a5377b..3980dad 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -39,6 +39,7 @@ #include <androidfw/ResourceTypes.h> +#include "DamageAccumulator.h" #include "Debug.h" #include "Matrix.h" #include "DeferredDisplayList.h" @@ -54,7 +55,6 @@ class SkRegion; namespace android { namespace uirenderer { -class DeferredDisplayList; class DisplayListOp; class DisplayListRenderer; class OpenGLRenderer; @@ -66,7 +66,7 @@ class ClipRectOp; class SaveLayerOp; class SaveOp; class RestoreToCountOp; -class DrawDisplayListOp; +class DrawRenderNodeOp; /** * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties. @@ -112,11 +112,8 @@ public: void computeOrdering(); - void deferNodeTree(DeferStateStruct& deferStruct); - void deferNodeInParent(DeferStateStruct& deferStruct, const int level); - - void replayNodeTree(ReplayStateStruct& replayStruct); - void replayNodeInParent(ReplayStateStruct& replayStruct, const int level); + void defer(DeferStateStruct& deferStruct, const int level); + void replay(ReplayStateStruct& replayStruct, const int level); ANDROID_API void output(uint32_t level = 1); ANDROID_API int getDebugSize(); @@ -125,6 +122,10 @@ public: return mDisplayListData && mDisplayListData->hasDrawOps; } + bool hasProjectionReceiver() const { + return mDisplayListData && mDisplayListData->projectionReceiveIndex >= 0; + } + const char* getName() const { return mName.string(); } @@ -148,7 +149,7 @@ public: mDirtyPropertyFields |= fields; } - const RenderProperties& properties() { + const RenderProperties& properties() const { return mProperties; } @@ -184,13 +185,18 @@ public: // UI thread only! ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) { mStagingAnimators.erase(animator); + // Force a sync of the staging property value + mDirtyPropertyFields |= animator->dirtyMask(); mNeedsAnimatorsSync = true; } +protected: + virtual void damageSelf(TreeInfo& info); + private: - typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair; + typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair; - static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) { + static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) { for (size_t i = 0; i < nodes.size(); i++) { if (nodes[i].key >= 0.0f) return i; } @@ -204,21 +210,29 @@ private: void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false); - void computeOrderingImpl(DrawDisplayListOp* opState, + void computeOrderingImpl(DrawRenderNodeOp* opState, const SkPath* outlineOfProjectionSurface, - Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface, + Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface, const mat4* transformFromProjectionSurface); template <class T> inline void setViewProperties(OpenGLRenderer& renderer, T& handler); - void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes); + void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes); template<class T> inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler); template <class T> - inline void issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes, + inline int issueOperationsOfNegZChildren( + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler); + template <class T> + inline void issueOperationsOfPosZChildren(int shadowRestoreTo, + const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, + OpenGLRenderer& renderer, T& handler); + template <class T> + inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes, ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler); template <class T> @@ -226,7 +240,7 @@ private: /** * Issue the RenderNode's operations into a handler, recursing for subtrees through - * DrawDisplayListOp's defer() or replay() methods + * DrawRenderNodeOp's defer() or replay() methods */ template <class T> inline void issueOperations(OpenGLRenderer& renderer, T& handler); @@ -246,9 +260,13 @@ private: }; void prepareTreeImpl(TreeInfo& info); - void pushStagingChanges(TreeInfo& info); + void pushStagingPropertiesChanges(TreeInfo& info); + void pushStagingDisplayListChanges(TreeInfo& info); void evaluateAnimations(TreeInfo& info); void prepareSubTree(TreeInfo& info, DisplayListData* subtree); + void applyLayerPropertiesToLayer(TreeInfo& info); + void prepareLayer(TreeInfo& info); + void pushLayerUpdate(TreeInfo& info); String8 mName; @@ -264,12 +282,16 @@ private: std::set< sp<BaseRenderNodeAnimator> > mStagingAnimators; std::vector< sp<BaseRenderNodeAnimator> > mAnimators; + // Owned by RT. Lifecycle is managed by prepareTree(), with the exception + // being in ~RenderNode() which may happen on any thread. + Layer* mLayer; + /** * Draw time state - these properties are only set and used during rendering */ // for projection surfaces, contains a list of all children items - Vector<DrawDisplayListOp*> mProjectedNodes; + Vector<DrawRenderNodeOp*> mProjectedNodes; }; // class RenderNode } /* namespace uirenderer */ diff --git a/libs/hwui/RenderProperties.cpp b/libs/hwui/RenderProperties.cpp index 5f7d4e3..8848b2f 100644 --- a/libs/hwui/RenderProperties.cpp +++ b/libs/hwui/RenderProperties.cpp @@ -21,16 +21,59 @@ #include <utils/Trace.h> #include <SkCanvas.h> +#include <SkColorFilter.h> #include <SkMatrix.h> #include <SkPath.h> #include <SkPathOps.h> #include "Matrix.h" +#include "OpenGLRenderer.h" #include "utils/MathUtils.h" namespace android { namespace uirenderer { +LayerProperties::LayerProperties() + : mType(kLayerTypeNone) + , mColorFilter(NULL) { + reset(); +} + +LayerProperties::~LayerProperties() { + setType(kLayerTypeNone); +} + +void LayerProperties::reset() { + mOpaque = false; + setFromPaint(NULL); +} + +bool LayerProperties::setColorFilter(SkColorFilter* filter) { + if (mColorFilter == filter) return false; + SkRefCnt_SafeAssign(mColorFilter, filter); + return true; +} + +bool LayerProperties::setFromPaint(const SkPaint* paint) { + bool changed = false; + SkXfermode::Mode mode; + int alpha; + OpenGLRenderer::getAlphaAndModeDirect(paint, &alpha, &mode); + changed |= setAlpha(static_cast<uint8_t>(alpha)); + changed |= setXferMode(mode); + changed |= setColorFilter(paint ? paint->getColorFilter() : NULL); + return changed; +} + +LayerProperties& LayerProperties::operator=(const LayerProperties& other) { + setType(other.type()); + setOpaque(other.opaque()); + setAlpha(other.alpha()); + setXferMode(other.xferMode()); + setColorFilter(other.colorFilter()); + return *this; +} + RenderProperties::PrimitiveFields::PrimitiveFields() : mClipToBounds(true) , mProjectBackwards(false) @@ -45,8 +88,7 @@ RenderProperties::PrimitiveFields::PrimitiveFields() , mLeft(0), mTop(0), mRight(0), mBottom(0) , mWidth(0), mHeight(0) , mPivotExplicitlySet(false) - , mMatrixOrPivotDirty(false) - , mCaching(false) { + , mMatrixOrPivotDirty(false) { } RenderProperties::ComputedFields::ComputedFields() @@ -73,6 +115,7 @@ RenderProperties& RenderProperties::operator=(const RenderProperties& other) { setStaticMatrix(other.getStaticMatrix()); setAnimationMatrix(other.getAnimationMatrix()); setCameraDistance(other.getCameraDistance()); + mLayerProperties = other.layerProperties(); // Force recalculation of the matrix, since other's dirty bit may be clear mPrimitiveFields.mMatrixOrPivotDirty = true; @@ -103,9 +146,9 @@ void RenderProperties::debugOutputProperties(const int level) const { } } - bool clipToBoundsNeeded = mPrimitiveFields.mCaching ? false : mPrimitiveFields.mClipToBounds; + bool clipToBoundsNeeded = layerProperties().type() != kLayerTypeNone ? false : mPrimitiveFields.mClipToBounds; if (mPrimitiveFields.mAlpha < 1) { - if (mPrimitiveFields.mCaching) { + if (layerProperties().type() != kLayerTypeNone) { ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); } else if (!mPrimitiveFields.mHasOverlappingRendering) { ALOGD("%*sScaleAlpha %.2f", level * 2, "", mPrimitiveFields.mAlpha); diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index c0e3ce7..227d56e 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -21,6 +21,7 @@ #include <vector> #include <cutils/compiler.h> #include <androidfw/ResourceTypes.h> +#include <utils/Log.h> #include <SkCamera.h> #include <SkMatrix.h> @@ -30,8 +31,10 @@ #include "Rect.h" #include "RevealClip.h" #include "Outline.h" +#include "utils/MathUtils.h" class SkBitmap; +class SkColorFilter; class SkPaint; namespace android { @@ -39,40 +42,125 @@ namespace uirenderer { class Matrix4; class RenderNode; +class RenderProperties; + +// The __VA_ARGS__ will be executed if a & b are not equal +#define RP_SET(a, b, ...) (a != b ? (a = b, ##__VA_ARGS__, true) : false) +#define RP_SET_AND_DIRTY(a, b) RP_SET(a, b, mPrimitiveFields.mMatrixOrPivotDirty = true) + +// Keep in sync with View.java:LAYER_TYPE_* +enum LayerType { + kLayerTypeNone = 0, + // Although we cannot build the software layer directly (must be done at + // record time), this information is used when applying alpha. + kLayerTypeSoftware = 1, + kLayerTypeRenderLayer = 2, + // TODO: LayerTypeSurfaceTexture? Maybe? +}; + +class ANDROID_API LayerProperties { +public: + bool setType(LayerType type) { + if (RP_SET(mType, type)) { + reset(); + return true; + } + return false; + } + + LayerType type() const { + return mType; + } + + bool setOpaque(bool opaque) { + return RP_SET(mOpaque, opaque); + } + + bool opaque() const { + return mOpaque; + } + + bool setAlpha(uint8_t alpha) { + return RP_SET(mAlpha, alpha); + } + + uint8_t alpha() const { + return mAlpha; + } + + bool setXferMode(SkXfermode::Mode mode) { + return RP_SET(mMode, mode); + } + + SkXfermode::Mode xferMode() const { + return mMode; + } + + bool setColorFilter(SkColorFilter* filter); + + SkColorFilter* colorFilter() const { + return mColorFilter; + } + + // Sets alpha, xfermode, and colorfilter from an SkPaint + // paint may be NULL, in which case defaults will be set + bool setFromPaint(const SkPaint* paint); + + bool needsBlending() const { + return !opaque() || alpha() < 255; + } + + LayerProperties& operator=(const LayerProperties& other); + +private: + LayerProperties(); + ~LayerProperties(); + void reset(); + + friend class RenderProperties; + + LayerType mType; + // Whether or not that Layer's content is opaque, doesn't include alpha + bool mOpaque; + uint8_t mAlpha; + SkXfermode::Mode mMode; + SkColorFilter* mColorFilter; +}; /* * Data structure that holds the properties for a RenderNode */ -class RenderProperties { +class ANDROID_API RenderProperties { public: RenderProperties(); virtual ~RenderProperties(); RenderProperties& operator=(const RenderProperties& other); - void setClipToBounds(bool clipToBounds) { - mPrimitiveFields.mClipToBounds = clipToBounds; + bool setClipToBounds(bool clipToBounds) { + return RP_SET(mPrimitiveFields.mClipToBounds, clipToBounds); } - void setProjectBackwards(bool shouldProject) { - mPrimitiveFields.mProjectBackwards = shouldProject; + bool setProjectBackwards(bool shouldProject) { + return RP_SET(mPrimitiveFields.mProjectBackwards, shouldProject); } - void setProjectionReceiver(bool shouldRecieve) { - mPrimitiveFields.mProjectionReceiver = shouldRecieve; + bool setProjectionReceiver(bool shouldRecieve) { + return RP_SET(mPrimitiveFields.mProjectionReceiver, shouldRecieve); } bool isProjectionReceiver() const { return mPrimitiveFields.mProjectionReceiver; } - void setStaticMatrix(const SkMatrix* matrix) { + bool setStaticMatrix(const SkMatrix* matrix) { delete mStaticMatrix; if (matrix) { mStaticMatrix = new SkMatrix(*matrix); } else { mStaticMatrix = NULL; } + return true; } // Can return NULL @@ -80,72 +168,61 @@ public: return mStaticMatrix; } - void setAnimationMatrix(const SkMatrix* matrix) { + bool setAnimationMatrix(const SkMatrix* matrix) { delete mAnimationMatrix; if (matrix) { mAnimationMatrix = new SkMatrix(*matrix); } else { mAnimationMatrix = NULL; } + return true; } - void setAlpha(float alpha) { + bool setAlpha(float alpha) { alpha = fminf(1.0f, fmaxf(0.0f, alpha)); - if (alpha != mPrimitiveFields.mAlpha) { - mPrimitiveFields.mAlpha = alpha; - } + return RP_SET(mPrimitiveFields.mAlpha, alpha); } float getAlpha() const { return mPrimitiveFields.mAlpha; } - void setHasOverlappingRendering(bool hasOverlappingRendering) { - mPrimitiveFields.mHasOverlappingRendering = hasOverlappingRendering; + bool setHasOverlappingRendering(bool hasOverlappingRendering) { + return RP_SET(mPrimitiveFields.mHasOverlappingRendering, hasOverlappingRendering); } bool hasOverlappingRendering() const { return mPrimitiveFields.mHasOverlappingRendering; } - void setElevation(float elevation) { - if (elevation != mPrimitiveFields.mElevation) { - mPrimitiveFields.mElevation = elevation; - // mMatrixOrPivotDirty not set, since matrix doesn't respect Z - } + bool setElevation(float elevation) { + return RP_SET(mPrimitiveFields.mElevation, elevation); + // Don't dirty matrix/pivot, since they don't respect Z } float getElevation() const { return mPrimitiveFields.mElevation; } - void setTranslationX(float translationX) { - if (translationX != mPrimitiveFields.mTranslationX) { - mPrimitiveFields.mTranslationX = translationX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setTranslationX(float translationX) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationX, translationX); } float getTranslationX() const { return mPrimitiveFields.mTranslationX; } - void setTranslationY(float translationY) { - if (translationY != mPrimitiveFields.mTranslationY) { - mPrimitiveFields.mTranslationY = translationY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setTranslationY(float translationY) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mTranslationY, translationY); } float getTranslationY() const { return mPrimitiveFields.mTranslationY; } - void setTranslationZ(float translationZ) { - if (translationZ != mPrimitiveFields.mTranslationZ) { - mPrimitiveFields.mTranslationZ = translationZ; - // mMatrixOrPivotDirty not set, since matrix doesn't respect Z - } + bool setTranslationZ(float translationZ) { + return RP_SET(mPrimitiveFields.mTranslationZ, translationZ); + // mMatrixOrPivotDirty not set, since matrix doesn't respect Z } float getTranslationZ() const { @@ -153,8 +230,8 @@ public: } // Animation helper - void setX(float value) { - setTranslationX(value - getLeft()); + bool setX(float value) { + return setTranslationX(value - getLeft()); } // Animation helper @@ -163,8 +240,8 @@ public: } // Animation helper - void setY(float value) { - setTranslationY(value - getTop()); + bool setY(float value) { + return setTranslationY(value - getTop()); } // Animation helper @@ -173,87 +250,82 @@ public: } // Animation helper - void setZ(float value) { - setTranslationZ(value - getElevation()); + bool setZ(float value) { + return setTranslationZ(value - getElevation()); } float getZ() const { return getElevation() + getTranslationZ(); } - void setRotation(float rotation) { - if (rotation != mPrimitiveFields.mRotation) { - mPrimitiveFields.mRotation = rotation; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotation(float rotation) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotation, rotation); } float getRotation() const { return mPrimitiveFields.mRotation; } - void setRotationX(float rotationX) { - if (rotationX != mPrimitiveFields.mRotationX) { - mPrimitiveFields.mRotationX = rotationX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotationX(float rotationX) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationX, rotationX); } float getRotationX() const { return mPrimitiveFields.mRotationX; } - void setRotationY(float rotationY) { - if (rotationY != mPrimitiveFields.mRotationY) { - mPrimitiveFields.mRotationY = rotationY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setRotationY(float rotationY) { + return RP_SET_AND_DIRTY(mPrimitiveFields.mRotationY, rotationY); } float getRotationY() const { return mPrimitiveFields.mRotationY; } - void setScaleX(float scaleX) { - if (scaleX != mPrimitiveFields.mScaleX) { - mPrimitiveFields.mScaleX = scaleX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setScaleX(float scaleX) { + LOG_ALWAYS_FATAL_IF(scaleX > 1000000, "invalid scaleX %e", scaleX); + return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleX, scaleX); } float getScaleX() const { return mPrimitiveFields.mScaleX; } - void setScaleY(float scaleY) { - if (scaleY != mPrimitiveFields.mScaleY) { - mPrimitiveFields.mScaleY = scaleY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + bool setScaleY(float scaleY) { + LOG_ALWAYS_FATAL_IF(scaleY > 1000000, "invalid scaleY %e", scaleY); + return RP_SET_AND_DIRTY(mPrimitiveFields.mScaleY, scaleY); } float getScaleY() const { return mPrimitiveFields.mScaleY; } - void setPivotX(float pivotX) { - mPrimitiveFields.mPivotX = pivotX; - mPrimitiveFields.mMatrixOrPivotDirty = true; - mPrimitiveFields.mPivotExplicitlySet = true; + bool setPivotX(float pivotX) { + if (RP_SET(mPrimitiveFields.mPivotX, pivotX) + || !mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + return true; + } + return false; } /* Note that getPivotX and getPivotY are adjusted by updateMatrix(), - * so the value returned mPrimitiveFields.may be stale if the RenderProperties has been - * mPrimitiveFields.modified since the last call to updateMatrix() + * so the value returned may be stale if the RenderProperties has been + * modified since the last call to updateMatrix() */ float getPivotX() const { return mPrimitiveFields.mPivotX; } - void setPivotY(float pivotY) { - mPrimitiveFields.mPivotY = pivotY; - mPrimitiveFields.mMatrixOrPivotDirty = true; - mPrimitiveFields.mPivotExplicitlySet = true; + bool setPivotY(float pivotY) { + if (RP_SET(mPrimitiveFields.mPivotY, pivotY) + || !mPrimitiveFields.mPivotExplicitlySet) { + mPrimitiveFields.mMatrixOrPivotDirty = true; + mPrimitiveFields.mPivotExplicitlySet = true; + return true; + } + return false; } float getPivotY() const { @@ -264,11 +336,13 @@ public: return mPrimitiveFields.mPivotExplicitlySet; } - void setCameraDistance(float distance) { + bool setCameraDistance(float distance) { if (distance != getCameraDistance()) { mPrimitiveFields.mMatrixOrPivotDirty = true; mComputedFields.mTransformCamera.setCameraLocation(0, 0, distance); + return true; } + return false; } float getCameraDistance() const { @@ -276,75 +350,73 @@ public: return const_cast<Sk3DView*>(&mComputedFields.mTransformCamera)->getCameraLocationZ(); } - void setLeft(int left) { - if (left != mPrimitiveFields.mLeft) { - mPrimitiveFields.mLeft = left; + bool setLeft(int left) { + if (RP_SET(mPrimitiveFields.mLeft, left)) { mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getLeft() const { return mPrimitiveFields.mLeft; } - void setTop(int top) { - if (top != mPrimitiveFields.mTop) { - mPrimitiveFields.mTop = top; + bool setTop(int top) { + if (RP_SET(mPrimitiveFields.mTop, top)) { mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getTop() const { return mPrimitiveFields.mTop; } - void setRight(int right) { - if (right != mPrimitiveFields.mRight) { - mPrimitiveFields.mRight = right; + bool setRight(int right) { + if (RP_SET(mPrimitiveFields.mRight, right)) { mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getRight() const { return mPrimitiveFields.mRight; } - void setBottom(int bottom) { - if (bottom != mPrimitiveFields.mBottom) { - mPrimitiveFields.mBottom = bottom; + bool setBottom(int bottom) { + if (RP_SET(mPrimitiveFields.mBottom, bottom)) { mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } float getBottom() const { return mPrimitiveFields.mBottom; } - void setLeftTop(int left, int top) { - if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop) { - mPrimitiveFields.mLeft = left; - mPrimitiveFields.mTop = top; - mPrimitiveFields.mWidth = mPrimitiveFields.mRight - mPrimitiveFields.mLeft; - mPrimitiveFields.mHeight = mPrimitiveFields.mBottom - mPrimitiveFields.mTop; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } - } + bool setLeftTop(int left, int top) { + bool leftResult = setLeft(left); + bool topResult = setTop(top); + return leftResult || topResult; } - void setLeftTopRightBottom(int left, int top, int right, int bottom) { + bool setLeftTopRightBottom(int left, int top, int right, int bottom) { if (left != mPrimitiveFields.mLeft || top != mPrimitiveFields.mTop || right != mPrimitiveFields.mRight || bottom != mPrimitiveFields.mBottom) { mPrimitiveFields.mLeft = left; @@ -356,31 +428,27 @@ public: if (!mPrimitiveFields.mPivotExplicitlySet) { mPrimitiveFields.mMatrixOrPivotDirty = true; } + return true; } + return false; } - void offsetLeftRight(float offset) { + bool offsetLeftRight(float offset) { if (offset != 0) { mPrimitiveFields.mLeft += offset; mPrimitiveFields.mRight += offset; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + return true; } + return false; } - void offsetTopBottom(float offset) { + bool offsetTopBottom(float offset) { if (offset != 0) { mPrimitiveFields.mTop += offset; mPrimitiveFields.mBottom += offset; - if (!mPrimitiveFields.mPivotExplicitlySet) { - mPrimitiveFields.mMatrixOrPivotDirty = true; - } + return true; } - } - - void setCaching(bool caching) { - mPrimitiveFields.mCaching = caching; + return false; } int getWidth() const { @@ -409,10 +477,6 @@ public: return mComputedFields.mTransformMatrix; } - bool getCaching() const { - return mPrimitiveFields.mCaching; - } - bool getClipToBounds() const { return mPrimitiveFields.mClipToBounds; } @@ -435,7 +499,7 @@ public: void debugOutputProperties(const int level) const; - ANDROID_API void updateMatrix(); + void updateMatrix(); bool hasClippingPath() const { return mPrimitiveFields.mRevealClip.willClip(); @@ -458,6 +522,23 @@ public: return mPrimitiveFields.mRevealClip; } + const LayerProperties& layerProperties() const { + return mLayerProperties; + } + + LayerProperties& mutateLayerProperties() { + return mLayerProperties; + } + + // Returns true if damage calculations should be clipped to bounds + // TODO: Figure out something better for getZ(), as children should still be + // clipped to this RP's bounds. But as we will damage -INT_MAX to INT_MAX + // for this RP's getZ() anyway, this can be optimized when we have a + // Z damage estimate instead of INT_MAX + bool getClipDamageToBounds() const { + return getClipToBounds() && (getZ() <= 0 || getOutline().isEmpty()); + } + private: // Rendering properties @@ -480,11 +561,11 @@ private: int mWidth, mHeight; bool mPivotExplicitlySet; bool mMatrixOrPivotDirty; - bool mCaching; } mPrimitiveFields; SkMatrix* mStaticMatrix; SkMatrix* mAnimationMatrix; + LayerProperties mLayerProperties; /** * These fields are all generated from other properties and are not set directly. diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp new file mode 100644 index 0000000..97f379d --- /dev/null +++ b/libs/hwui/RenderState.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "RenderState.h" + +namespace android { +namespace uirenderer { + +RenderState::RenderState() + : mCaches(NULL) + , mViewportWidth(0) + , mViewportHeight(0) + , mFramebuffer(0) { +} + +RenderState::~RenderState() { +} + +void RenderState::onGLContextCreated() { + // This is delayed because the first access of Caches makes GL calls + mCaches = &Caches::getInstance(); + mCaches->init(); +} + +void RenderState::setViewport(GLsizei width, GLsizei height) { + mViewportWidth = width; + mViewportHeight = height; + glViewport(0, 0, mViewportWidth, mViewportHeight); +} + + +void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { + *outWidth = mViewportWidth; + *outHeight = mViewportHeight; +} + +void RenderState::bindFramebuffer(GLuint fbo) { + if (mFramebuffer != fbo) { + mFramebuffer = fbo; + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + } +} + +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); +} + +void RenderState::interruptForFunctorInvoke() { + if (mCaches->currentProgram) { + if (mCaches->currentProgram->isInUse()) { + mCaches->currentProgram->remove(); + mCaches->currentProgram = NULL; + } + } + mCaches->resetActiveTexture(); + mCaches->unbindMeshBuffer(); + mCaches->unbindIndicesBuffer(); + mCaches->resetVertexPointers(); + mCaches->disableTexCoordsVertexArray(); + debugOverdraw(false, false); +} + +void RenderState::resumeFromFunctorInvoke() { + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST); + mCaches->enableScissor(); + mCaches->resetScissor(); + + mCaches->activeTexture(0); + mCaches->resetBoundTextures(); + + mCaches->blend = true; + glEnable(GL_BLEND); + glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode); + glBlendEquation(GL_FUNC_ADD); +} + +void RenderState::debugOverdraw(bool enable, bool clear) { + if (mCaches->debugOverdraw && mFramebuffer == 0) { + if (clear) { + mCaches->disableScissor(); + mCaches->stencil.clear(); + } + if (enable) { + mCaches->stencil.enableDebugWrite(); + } else { + mCaches->stencil.disable(); + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h new file mode 100644 index 0000000..f7116e2 --- /dev/null +++ b/libs/hwui/RenderState.h @@ -0,0 +1,70 @@ +/* + * 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 RENDERSTATE_H +#define RENDERSTATE_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +#include <private/hwui/DrawGlInfo.h> + +#include "Caches.h" +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +namespace renderthread { +class RenderThread; +} + +// TODO: Replace Cache's GL state tracking with this. For now it's more a thin +// wrapper of Caches for users to migrate to. +class RenderState { + PREVENT_COPY_AND_ASSIGN(RenderState); +public: + void onGLContextCreated(); + + void setViewport(GLsizei width, GLsizei height); + void getViewport(GLsizei* outWidth, GLsizei* outHeight); + + void bindFramebuffer(GLuint fbo); + GLint getFramebuffer() { return mFramebuffer; } + + void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); + + void debugOverdraw(bool enable, bool clear); + +private: + friend class renderthread::RenderThread; + + void interruptForFunctorInvoke(); + void resumeFromFunctorInvoke(); + + RenderState(); + ~RenderState(); + + Caches* mCaches; + + GLsizei mViewportWidth; + GLsizei mViewportHeight; + GLuint mFramebuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* RENDERSTATE_H */ diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h index 23cab0e..2ec99c9 100644 --- a/libs/hwui/Renderer.h +++ b/libs/hwui/Renderer.h @@ -129,21 +129,6 @@ public: */ virtual void finish() = 0; - /** - * This method must be invoked before handing control over to a draw functor. - * See callDrawGLFunction() for instance. - * - * This command must not be recorded inside display lists. - */ - virtual void interrupt() = 0; - - /** - * This method must be invoked after getting control back from a draw functor. - * - * This command must not be recorded inside display lists. - */ - virtual void resume() = 0; - // ---------------------------------------------------------------------------- // Canvas state operations // ---------------------------------------------------------------------------- @@ -170,8 +155,8 @@ public: virtual void scale(float sx, float sy) = 0; virtual void skew(float sx, float sy) = 0; - virtual void setMatrix(const SkMatrix* matrix) = 0; - virtual void concatMatrix(const SkMatrix* matrix) = 0; + virtual void setMatrix(const SkMatrix& matrix) = 0; + virtual void concatMatrix(const SkMatrix& matrix) = 0; // clip virtual const Rect& getLocalClipBounds() const = 0; @@ -193,7 +178,7 @@ public: // Bitmap-based virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top, const SkPaint* paint) = 0; - virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix* matrix, + virtual status_t drawBitmap(const SkBitmap* bitmap, const SkMatrix& matrix, const SkPaint* paint) = 0; virtual status_t drawBitmap(const SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, @@ -233,7 +218,7 @@ public: // Canvas draw operations - special // ---------------------------------------------------------------------------- virtual status_t drawLayer(Layer* layer, float x, float y) = 0; - virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, + virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) = 0; // TODO: rename for consistency diff --git a/libs/hwui/StatefulBaseRenderer.cpp b/libs/hwui/StatefulBaseRenderer.cpp index f2e28e1..95c0ee5 100644 --- a/libs/hwui/StatefulBaseRenderer.cpp +++ b/libs/hwui/StatefulBaseRenderer.cpp @@ -125,20 +125,16 @@ void StatefulBaseRenderer::skew(float sx, float sy) { mSnapshot->transform->skew(sx, sy); } -void StatefulBaseRenderer::setMatrix(const SkMatrix* matrix) { - if (matrix) { - mSnapshot->transform->load(*matrix); - } else { - mSnapshot->transform->loadIdentity(); - } +void StatefulBaseRenderer::setMatrix(const SkMatrix& matrix) { + mSnapshot->transform->load(matrix); } void StatefulBaseRenderer::setMatrix(const Matrix4& matrix) { mSnapshot->transform->load(matrix); } -void StatefulBaseRenderer::concatMatrix(const SkMatrix* matrix) { - mat4 transform(*matrix); +void StatefulBaseRenderer::concatMatrix(const SkMatrix& matrix) { + mat4 transform(matrix); mSnapshot->transform->multiply(transform); } diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index dbb1d85..e8e024f 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -76,9 +76,9 @@ public: virtual void scale(float sx, float sy); virtual void skew(float sx, float sy); - virtual void setMatrix(const SkMatrix* matrix); + virtual void setMatrix(const SkMatrix& matrix); void setMatrix(const Matrix4& matrix); // internal only convenience method - virtual void concatMatrix(const SkMatrix* matrix); + virtual void concatMatrix(const SkMatrix& matrix); void concatMatrix(const Matrix4& matrix); // internal only convenience method // Clip diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp index 37d5347..08b54ff 100644 --- a/libs/hwui/TessellationCache.cpp +++ b/libs/hwui/TessellationCache.cpp @@ -39,6 +39,8 @@ namespace uirenderer { TessellationCache::Description::Description() : type(kNone) + , scaleX(1.0f) + , scaleY(1.0f) , aa(false) , cap(SkPaint::kDefault_Cap) , style(SkPaint::kFill_Style) @@ -46,21 +48,13 @@ TessellationCache::Description::Description() memset(&shape, 0, sizeof(Shape)); } -TessellationCache::Description::Description(Type type) +TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint) : type(type) - , aa(false) - , cap(SkPaint::kDefault_Cap) - , style(SkPaint::kFill_Style) - , strokeWidth(1.0f) { - memset(&shape, 0, sizeof(Shape)); -} - -TessellationCache::Description::Description(Type type, const SkPaint* paint) - : type(type) - , aa(paint->isAntiAlias()) - , cap(paint->getStrokeCap()) - , style(paint->getStyle()) - , strokeWidth(paint->getStrokeWidth()) { + , aa(paint.isAntiAlias()) + , cap(paint.getStrokeCap()) + , style(paint.getStyle()) + , strokeWidth(paint.getStrokeWidth()) { + PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY); memset(&shape, 0, sizeof(Shape)); } @@ -70,10 +64,20 @@ hash_t TessellationCache::Description::hash() const { hash = JenkinsHashMix(hash, cap); hash = JenkinsHashMix(hash, style); hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); + hash = JenkinsHashMix(hash, android::hash_type(scaleX)); + hash = JenkinsHashMix(hash, android::hash_type(scaleY)); hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); return JenkinsHashWhiten(hash); } +void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const { + matrix->loadScale(scaleX, scaleY, 1.0f); + paint->setAntiAlias(aa); + paint->setStrokeCap(cap); + paint->setStyle(style); + paint->setStrokeWidth(strokeWidth); +} + TessellationCache::ShadowDescription::ShadowDescription() : nodeKey(NULL) { memset(&matrixData, 0, 16 * sizeof(float)); @@ -96,20 +100,15 @@ hash_t TessellationCache::ShadowDescription::hash() const { class TessellationCache::TessellationTask : public Task<VertexBuffer*> { public: - TessellationTask(Tessellator tessellator, const Description& description, - const SkPaint* paint) + TessellationTask(Tessellator tessellator, const Description& description) : tessellator(tessellator) - , description(description) - , paint(*paint) { + , description(description) { } ~TessellationTask() {} Tessellator tessellator; Description description; - - //copied, since input paint may not be immutable - const SkPaint paint; }; class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> { @@ -121,7 +120,7 @@ public: virtual void onProcess(const sp<Task<VertexBuffer*> >& task) { TessellationTask* t = static_cast<TessellationTask*>(task.get()); ATRACE_NAME("shape tessellation"); - VertexBuffer* buffer = t->tessellator(t->description, t->paint); + VertexBuffer* buffer = t->tessellator(t->description); t->setResult(buffer); } }; @@ -354,12 +353,11 @@ void TessellationCache::setMaxSize(uint32_t maxSize) { void TessellationCache::trim() { - // uint32_t size = getSize(); - // while (size > mMaxSize) { - // size -= mCache.peekOldestValue()->getSize(); - // mCache.removeOldest(); - // } - mCache.clear(); // Workaround caching tessellation corruption + uint32_t size = getSize(); + while (size > mMaxSize) { + size -= mCache.peekOldestValue()->getSize(); + mCache.removeOldest(); + } mShadowCache.clear(); } @@ -417,21 +415,12 @@ void TessellationCache::getShadowBuffers(const Matrix4* drawTransform, const Rec // Tessellation precaching /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint, - float scaleX, float scaleY) { - VertexBuffer* buffer = new VertexBuffer(); - Matrix4 matrix; - matrix.loadScale(scaleX, scaleY, 1); - PathTessellator::tessellatePath(path, paint, matrix, *buffer); - return buffer; -} - TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( - const Description& entry, Tessellator tessellator, const SkPaint* paint) { + const Description& entry, Tessellator tessellator) { Buffer* buffer = mCache.get(entry); if (!buffer) { // not cached, enqueue a task to fill the buffer - sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint); + sp<TessellationTask> task = new TessellationTask(tessellator, entry); buffer = new Buffer(task); if (mProcessor == NULL) { @@ -443,43 +432,49 @@ TessellationCache::Buffer* TessellationCache::getOrCreateBuffer( return buffer; } +static VertexBuffer* tessellatePath(const TessellationCache::Description& description, + const SkPath& path) { + Matrix4 matrix; + SkPaint paint; + description.setupMatrixAndPaint(&matrix, &paint); + VertexBuffer* buffer = new VertexBuffer(); + PathTessellator::tessellatePath(path, &paint, matrix, *buffer); + return buffer; +} + /////////////////////////////////////////////////////////////////////////////// -// Rounded rects +// RoundRect /////////////////////////////////////////////////////////////////////////////// -static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description, - const SkPaint& paint) { - SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth, - description.shape.roundRect.mHeight); - float rx = description.shape.roundRect.mRx; - float ry = description.shape.roundRect.mRy; - if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) { - float outset = paint.getStrokeWidth() / 2; +static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) { + SkRect rect = SkRect::MakeWH(description.shape.roundRect.width, + description.shape.roundRect.height); + float rx = description.shape.roundRect.rx; + float ry = description.shape.roundRect.ry; + if (description.style == SkPaint::kStrokeAndFill_Style) { + float outset = description.strokeWidth / 2; rect.outset(outset, outset); rx += outset; ry += outset; } SkPath path; path.addRoundRect(rect, rx, ry); - return tessellatePath(path, &paint, - description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY); + return tessellatePath(description, path); } -TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - Description entry(Description::kRoundRect, paint); - entry.shape.roundRect.mWidth = width; - entry.shape.roundRect.mHeight = height; - entry.shape.roundRect.mRx = rx; - entry.shape.roundRect.mRy = ry; - PathTessellator::extractTessellationScales(transform, - &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY); - - return getOrCreateBuffer(entry, &tessellateRoundRect, paint); +TessellationCache::Buffer* TessellationCache::getRoundRectBuffer( + const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + Description entry(Description::kRoundRect, transform, paint); + entry.shape.roundRect.width = width; + entry.shape.roundRect.height = height; + entry.shape.roundRect.rx = rx; + entry.shape.roundRect.ry = ry; + return getOrCreateBuffer(entry, &tessellateRoundRect); } -const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer(); +const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer(); } }; // namespace uirenderer diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index d4ff943..688a699 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -50,30 +50,28 @@ public: enum Type { kNone, kRoundRect, - kAmbientShadow, - kSpotShadow }; Type type; + float scaleX; + float scaleY; bool aa; SkPaint::Cap cap; SkPaint::Style style; float strokeWidth; union Shape { struct RoundRect { - float mScaleX; - float mScaleY; - float mWidth; - float mHeight; - float mRx; - float mRy; + float width; + float height; + float rx; + float ry; } roundRect; } shape; Description(); - Description(Type type); - Description(Type type, const SkPaint* paint); + Description(Type type, const Matrix4& transform, const SkPaint& paint); hash_t hash() const; + void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const; }; struct ShadowDescription { @@ -123,12 +121,12 @@ public: // TODO: precache/get for Oval, Lines, Points, etc. - void precacheRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint) { - getRoundRectBuffer(transform, width, height, rx, ry, paint); + void precacheRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry) { + getRoundRectBuffer(transform, paint, width, height, rx, ry); } - const VertexBuffer* getRoundRect(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); + const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); void precacheShadows(const Matrix4* drawTransform, const Rect& localClip, bool opaque, const SkPath* casterPerimeter, @@ -146,14 +144,14 @@ private: class TessellationTask; class TessellationProcessor; + typedef VertexBuffer* (*Tessellator)(const Description&); - typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&); + Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height); + Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint, + float width, float height, float rx, float ry); - Buffer* getRoundRectBuffer(const Matrix4& transform, - float width, float height, float rx, float ry, const SkPaint* paint); - - Buffer* getOrCreateBuffer(const Description& entry, - Tessellator tessellator, const SkPaint* paint); + Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator); uint32_t mSize; uint32_t mMaxSize; diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 60b4b96..9212d0a 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -288,19 +288,19 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo switch (bitmap->config()) { case SkBitmap::kA8_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_ALPHA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); texture->blend = true; break; case SkBitmap::kRGB_565_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGB, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_SHORT_5_6_5, bitmap->getPixels()); texture->blend = false; break; case SkBitmap::kARGB_8888_Config: glPixelStorei(GL_UNPACK_ALIGNMENT, bitmap->bytesPerPixel()); - uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), + uploadToTexture(resize, GL_RGBA, bitmap->rowBytesAsPixels(), bitmap->bytesPerPixel(), texture->width, texture->height, GL_UNSIGNED_BYTE, bitmap->getPixels()); // Do this after calling getPixels() to make sure Skia's deferred // decoding happened @@ -333,34 +333,55 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height) { SkBitmap rgbaBitmap; - rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType()); - rgbaBitmap.allocPixels(); + rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType())); rgbaBitmap.eraseColor(0); SkCanvas canvas(rgbaBitmap); canvas.drawBitmap(*bitmap, 0.0f, 0.0f, NULL); - uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), width, height, - GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); + uploadToTexture(resize, GL_RGBA, rgbaBitmap.rowBytesAsPixels(), rgbaBitmap.bytesPerPixel(), + width, height, GL_UNSIGNED_BYTE, rgbaBitmap.getPixels()); } -void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, +void TextureCache::uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data) { - // TODO: With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer - // if the stride doesn't match the width const bool useStride = stride != width && Extensions::getInstance().hasUnpackRowLength(); - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); - } + if ((stride == width) || useStride) { + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, stride); + } - if (resize) { - glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, data); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); + } + + if (useStride) { + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + } } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, data); - } + // With OpenGL ES 2.0 we need to copy the bitmap in a temporary buffer + // if the stride doesn't match the width + + GLvoid * temp = (GLvoid *) malloc(width * height * bpp); + if (!temp) return; + + uint8_t * pDst = (uint8_t *)temp; + uint8_t * pSrc = (uint8_t *)data; + for (GLsizei i = 0; i < height; i++) { + memcpy(pDst, pSrc, width * bpp); + pDst += width * bpp; + pSrc += stride * bpp; + } + + if (resize) { + glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, type, temp); + } else { + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, format, type, temp); + } - if (useStride) { - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + free(temp); } } diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index e5b5c1a..61db5b0 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -142,7 +142,7 @@ private: void generateTexture(const SkBitmap* bitmap, Texture* texture, bool regenerate = false); void uploadLoFiTexture(bool resize, const SkBitmap* bitmap, uint32_t width, uint32_t height); - void uploadToTexture(bool resize, GLenum format, GLsizei stride, + void uploadToTexture(bool resize, GLenum format, GLsizei stride, GLsizei bpp, GLsizei width, GLsizei height, GLenum type, const GLvoid * data); void init(); diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h index 8355f83..249e525 100644 --- a/libs/hwui/TreeInfo.h +++ b/libs/hwui/TreeInfo.h @@ -16,13 +16,20 @@ #ifndef TREEINFO_H #define TREEINFO_H +#include <string> + #include <utils/Timers.h> +#include "DamageAccumulator.h" +#include "utils/Macros.h" + namespace android { namespace uirenderer { class BaseRenderNodeAnimator; class AnimationListener; +class OpenGLRenderer; +class RenderState; class AnimationHook { public: @@ -31,21 +38,60 @@ protected: ~AnimationHook() {} }; -struct TreeInfo { - // The defaults here should be safe for everyone but DrawFrameTask to use as-is. - TreeInfo() - : frameTimeMs(0) +class ErrorHandler { +public: + virtual void onError(const std::string& message) = 0; +protected: + ~ErrorHandler() {} +}; + +// 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, RenderState& renderState) + : mode(mode) + , frameTimeMs(0) , animationHook(NULL) - , prepareTextures(false) - , performStagingPush(true) - , evaluateAnimations(false) + , prepareTextures(mode == MODE_FULL) + , damageAccumulator(NullDamageAccumulator::instance()) + , renderState(renderState) + , renderer(NULL) + , errorHandler(NULL) {} + 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; + // Must not be null + IDamageAccumulator* damageAccumulator; + RenderState& renderState; + // The renderer that will be drawing the next frame. Use this to push any + // layer updates or similar. May be NULL. + OpenGLRenderer* renderer; + ErrorHandler* errorHandler; struct Out { Out() diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 9ebee1d..281a8e1 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -18,332 +18,31 @@ #include "CanvasContext.h" -#include <cutils/properties.h> #include <private/hwui/DrawGlInfo.h> #include <strings.h> +#include "EglManager.h" #include "RenderThread.h" #include "../Caches.h" #include "../DeferredLayerUpdater.h" +#include "../RenderState.h" #include "../LayerRenderer.h" #include "../OpenGLRenderer.h" #include "../Stencil.h" -#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" -#define GLES_VERSION 2 - -// Android-specific addition that is used to show when frames began in systrace -EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); - namespace android { namespace uirenderer { namespace renderthread { -#define ERROR_CASE(x) case x: return #x; -static const char* egl_error_str(EGLint error) { - switch (error) { - ERROR_CASE(EGL_SUCCESS) - ERROR_CASE(EGL_NOT_INITIALIZED) - ERROR_CASE(EGL_BAD_ACCESS) - ERROR_CASE(EGL_BAD_ALLOC) - ERROR_CASE(EGL_BAD_ATTRIBUTE) - ERROR_CASE(EGL_BAD_CONFIG) - ERROR_CASE(EGL_BAD_CONTEXT) - ERROR_CASE(EGL_BAD_CURRENT_SURFACE) - ERROR_CASE(EGL_BAD_DISPLAY) - ERROR_CASE(EGL_BAD_MATCH) - ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) - ERROR_CASE(EGL_BAD_NATIVE_WINDOW) - ERROR_CASE(EGL_BAD_PARAMETER) - ERROR_CASE(EGL_BAD_SURFACE) - ERROR_CASE(EGL_CONTEXT_LOST) - default: - return "Unknown error"; - } -} -static const char* egl_error_str() { - return egl_error_str(eglGetError()); -} - -static bool load_dirty_regions_property() { - char buf[PROPERTY_VALUE_MAX]; - int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); - return !strncasecmp("true", buf, len); -} - -// This class contains the shared global EGL objects, such as EGLDisplay -// and EGLConfig, which are re-used by CanvasContext -class GlobalContext { -public: - static GlobalContext* get(); - - // Returns true on success, false on failure - void initialize(); - - bool hasContext(); - - void usePBufferSurface(); - EGLSurface createSurface(EGLNativeWindowType window); - void destroySurface(EGLSurface surface); - - void destroy(); - - bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } - // Returns true if the current surface changed, false if it was already current - bool makeCurrent(EGLSurface surface); - void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); - void swapBuffers(EGLSurface surface); - - bool enableDirtyRegions(EGLSurface surface); - - void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); - -private: - GlobalContext(); - // GlobalContext is never destroyed, method is purposely not implemented - ~GlobalContext(); - - void loadConfig(); - void createContext(); - void initAtlas(); - - static GlobalContext* sContext; - - EGLDisplay mEglDisplay; - EGLConfig mEglConfig; - EGLContext mEglContext; - EGLSurface mPBufferSurface; - - const bool mRequestDirtyRegions; - bool mCanSetDirtyRegions; - - EGLSurface mCurrentSurface; - - sp<GraphicBuffer> mAtlasBuffer; - int64_t* mAtlasMap; - size_t mAtlasMapSize; -}; - -GlobalContext* GlobalContext::sContext = 0; - -GlobalContext* GlobalContext::get() { - if (!sContext) { - sContext = new GlobalContext(); - } - return sContext; -} - -GlobalContext::GlobalContext() - : mEglDisplay(EGL_NO_DISPLAY) - , mEglConfig(0) - , mEglContext(EGL_NO_CONTEXT) - , mPBufferSurface(EGL_NO_SURFACE) - , mRequestDirtyRegions(load_dirty_regions_property()) - , mCurrentSurface(EGL_NO_SURFACE) - , mAtlasMap(NULL) - , mAtlasMapSize(0) { - mCanSetDirtyRegions = mRequestDirtyRegions; - ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); -} - -void GlobalContext::initialize() { - if (hasContext()) return; - - mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); - LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); - - EGLint major, minor; - LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, - "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); - - ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); - - loadConfig(); - createContext(); - usePBufferSurface(); - Caches::getInstance().init(); - initAtlas(); -} - -bool GlobalContext::hasContext() { - return mEglDisplay != EGL_NO_DISPLAY; -} - -void GlobalContext::loadConfig() { - EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; - EGLint attribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, - EGL_RED_SIZE, 8, - EGL_GREEN_SIZE, 8, - EGL_BLUE_SIZE, 8, - EGL_ALPHA_SIZE, 8, - EGL_DEPTH_SIZE, 0, - EGL_CONFIG_CAVEAT, EGL_NONE, - EGL_STENCIL_SIZE, Stencil::getStencilSize(), - EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, - EGL_NONE - }; - - EGLint num_configs = 1; - if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) - || num_configs != 1) { - // Failed to get a valid config - if (mCanSetDirtyRegions) { - ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); - // Try again without dirty regions enabled - mCanSetDirtyRegions = false; - loadConfig(); - } else { - LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); - } - } -} - -void GlobalContext::createContext() { - EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; - mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); - LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, - "Failed to create context, error = %s", egl_error_str()); -} - -void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - - // Already initialized - if (mAtlasBuffer.get()) { - ALOGW("Multiple calls to setTextureAtlas!"); - delete map; - return; - } - - mAtlasBuffer = buffer; - mAtlasMap = map; - mAtlasMapSize = mapSize; - - if (hasContext()) { - usePBufferSurface(); - initAtlas(); - } -} - -void GlobalContext::initAtlas() { - if (mAtlasBuffer.get()) { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); - } -} - -void GlobalContext::usePBufferSurface() { - LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, - "usePBufferSurface() called on uninitialized GlobalContext!"); - - if (mPBufferSurface == EGL_NO_SURFACE) { - EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; - mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); - } - makeCurrent(mPBufferSurface); -} - -EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) { - initialize(); - return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); -} - -void GlobalContext::destroySurface(EGLSurface surface) { - if (isCurrent(surface)) { - makeCurrent(EGL_NO_SURFACE); - } - if (!eglDestroySurface(mEglDisplay, surface)) { - ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); - } -} - -void GlobalContext::destroy() { - if (mEglDisplay == EGL_NO_DISPLAY) return; - - usePBufferSurface(); - if (Caches::hasInstance()) { - Caches::getInstance().terminate(); - } - - eglDestroyContext(mEglDisplay, mEglContext); - eglDestroySurface(mEglDisplay, mPBufferSurface); - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglTerminate(mEglDisplay); - eglReleaseThread(); - - mEglDisplay = EGL_NO_DISPLAY; - mEglContext = EGL_NO_CONTEXT; - mPBufferSurface = EGL_NO_SURFACE; - mCurrentSurface = EGL_NO_SURFACE; -} - -bool GlobalContext::makeCurrent(EGLSurface surface) { - if (isCurrent(surface)) return false; - - if (surface == EGL_NO_SURFACE) { - // If we are setting EGL_NO_SURFACE we don't care about any of the potential - // return errors, which would only happen if mEglDisplay had already been - // destroyed in which case the current context is already NO_CONTEXT - eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { - LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", - (void*)surface, egl_error_str()); - } - mCurrentSurface = surface; - return true; -} - -void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { - LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, - "Tried to beginFrame on EGL_NO_SURFACE!"); - makeCurrent(surface); - if (width) { - eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); - } - if (height) { - eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); - } - eglBeginFrame(mEglDisplay, surface); -} - -void GlobalContext::swapBuffers(EGLSurface surface) { - eglSwapBuffers(mEglDisplay, surface); - EGLint err = eglGetError(); - LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, - "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); -} - -bool GlobalContext::enableDirtyRegions(EGLSurface surface) { - if (!mRequestDirtyRegions) return false; - - if (mCanSetDirtyRegions) { - if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { - ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", - (void*) surface, egl_error_str()); - return false; - } - return true; - } - // Perhaps it is already enabled? - EGLint value; - if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { - ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", - (void*) surface, egl_error_str()); - return false; - } - return value == EGL_BUFFER_PRESERVED; -} - -CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode) - : mRenderThread(RenderThread::getInstance()) +CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode) + : mRenderThread(thread) + , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) , mDirtyRegionsEnabled(false) , mOpaque(!translucent) - , mCanvas(0) + , mCanvas(NULL) , mHaveNewSurface(false) , mRootRenderNode(rootRenderNode) { - mGlobalContext = GlobalContext::get(); } CanvasContext::~CanvasContext() { @@ -363,19 +62,16 @@ void CanvasContext::setSurface(ANativeWindow* window) { mNativeWindow = window; if (mEglSurface != EGL_NO_SURFACE) { - mGlobalContext->destroySurface(mEglSurface); + mEglManager.destroySurface(mEglSurface); mEglSurface = EGL_NO_SURFACE; } if (window) { - mEglSurface = mGlobalContext->createSurface(window); - LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "Failed to create EGLSurface for window %p, eglErr = %s", - (void*) window, egl_error_str()); + mEglSurface = mEglManager.createSurface(window); } if (mEglSurface != EGL_NO_SURFACE) { - mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface); + mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); mHaveNewSurface = true; makeCurrent(); } else { @@ -384,7 +80,7 @@ void CanvasContext::setSurface(ANativeWindow* window) { } void CanvasContext::swapBuffers() { - mGlobalContext->swapBuffers(mEglSurface); + mEglManager.swapBuffers(mEglSurface); mHaveNewSurface = false; } @@ -397,7 +93,7 @@ void CanvasContext::requireSurface() { bool CanvasContext::initialize(ANativeWindow* window) { if (mCanvas) return false; setSurface(window); - mCanvas = new OpenGLRenderer(); + mCanvas = new OpenGLRenderer(mRenderThread.renderState()); mCanvas->initProperties(); return true; } @@ -424,7 +120,7 @@ void CanvasContext::setOpaque(bool opaque) { void CanvasContext::makeCurrent() { // TODO: Figure out why this workaround is needed, see b/13913604 // In the meantime this matches the behavior of GLRenderer, so it is not a regression - mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface); + mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface); } void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) { @@ -439,6 +135,8 @@ void CanvasContext::prepareTree(TreeInfo& info) { mRenderThread.removeFrameCallback(this); info.frameTimeMs = mRenderThread.timeLord().frameTimeMs(); + info.damageAccumulator = &mDamageAccumulator; + info.renderer = mCanvas; mRootRenderNode->prepareTree(info); int runningBehind = 0; @@ -450,9 +148,7 @@ void CanvasContext::prepareTree(TreeInfo& info) { info.out.canDrawThisFrame = !runningBehind; if (info.out.hasAnimations || !info.out.canDrawThisFrame) { - if (info.out.hasFunctors) { - info.out.requiresUiRedraw = true; - } else if (!info.out.requiresUiRedraw) { + if (!info.out.requiresUiRedraw) { // If animationsNeedsRedraw is set don't bother posting for an RT anim // as we will just end up fighting the UI thread. mRenderThread.postFrameCallback(this); @@ -465,33 +161,36 @@ 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!"); + "drawRenderNode called on a context with no canvas or surface!"); profiler().markPlaybackStart(); + SkRect dirty; + mDamageAccumulator.finish(&dirty); + EGLint width, height; - mGlobalContext->beginFrame(mEglSurface, &width, &height); + mEglManager.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); } Rect outBounds; - status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds); + status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds); profiler().draw(mCanvas); @@ -516,40 +215,35 @@ void CanvasContext::doFrame() { profiler().startFrame(); - TreeInfo info; - info.evaluateAnimations = true; - info.performStagingPush = false; + TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState()); info.prepareTextures = false; prepareTree(info); if (info.out.canDrawThisFrame) { - draw(NULL); + draw(); } } -void CanvasContext::invokeFunctor(Functor* functor) { +void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) { ATRACE_CALL(); DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext; - if (mGlobalContext->hasContext()) { - requireGlContext(); + if (thread.eglManager().hasEglContext()) { + thread.eglManager().requireGlContext(); mode = DrawGlInfo::kModeProcess; } - (*functor)(mode, NULL); - if (mCanvas) { - mCanvas->resume(); - } + thread.renderState().invokeFunctor(functor, mode, NULL); } bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { requireGlContext(); - TreeInfo info; + TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState()); layer->apply(info); - return LayerRenderer::copyLayer(layer->backingLayer(), bitmap); + return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap); } void CanvasContext::flushCaches(Caches::FlushMode flushMode) { - if (mGlobalContext->hasContext()) { + if (mEglManager.hasEglContext()) { requireGlContext(); Caches::getInstance().flush(flushMode); } @@ -562,25 +256,25 @@ void CanvasContext::runWithGlContext(RenderTask* task) { Layer* CanvasContext::createRenderLayer(int width, int height) { requireSurface(); - return LayerRenderer::createRenderLayer(width, height); + return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height); } Layer* CanvasContext::createTextureLayer() { requireSurface(); - return LayerRenderer::createTextureLayer(); + return LayerRenderer::createTextureLayer(mRenderThread.renderState()); } void CanvasContext::requireGlContext() { if (mEglSurface != EGL_NO_SURFACE) { makeCurrent(); } else { - mGlobalContext->usePBufferSurface(); + mEglManager.usePBufferSurface(); } } -void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize) { - GlobalContext::get()->setTextureAtlas(buffer, map, mapSize); +void CanvasContext::setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) { + thread.eglManager().setTextureAtlas(buffer, map, mapSize); } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 00c5bf0..d2ce1a6 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" @@ -40,13 +41,13 @@ class Layer; namespace renderthread { -class GlobalContext; +class EglManager; // This per-renderer class manages the bridge between the global EGL context // and the render surface. class CanvasContext : public IFrameCallback { public: - CanvasContext(bool translucent, RenderNode* rootRenderNode); + CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode); virtual ~CanvasContext(); bool initialize(ANativeWindow* window); @@ -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 @@ -67,15 +68,15 @@ public: void flushCaches(Caches::FlushMode flushMode); - void invokeFunctor(Functor* functor); + static void invokeFunctor(RenderThread& thread, Functor* functor); void runWithGlContext(RenderTask* task); Layer* createRenderLayer(int width, int height); Layer* createTextureLayer(); - ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer, - int64_t* map, size_t mapSize); + ANDROID_API static void setTextureAtlas(RenderThread& thread, + const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); void notifyFramePending(); @@ -90,8 +91,8 @@ private: void requireGlContext(); - GlobalContext* mGlobalContext; RenderThread& mRenderThread; + EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; EGLSurface mEglSurface; bool mDirtyRegionsEnabled; @@ -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..fddffd5 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, mRenderThread->renderState()); 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,37 +120,25 @@ 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); } mLayers.clear(); - if (info.out.hasAnimations) { - // TODO: Uh... crap? - } 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; } } // If prepareTextures is false, we ran out of texture cache space - return !info.out.hasFunctors && info.prepareTextures; + return info.prepareTextures; } void DrawFrameTask::unblockUiThread() { diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index d4129b6..243cc5d 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -41,7 +41,7 @@ class RenderThread; enum SyncResult { kSync_OK = 0, - kSync_UIRedrawRequired = 1 << 1, + kSync_UIRedrawRequired = 1 << 0, }; /* @@ -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/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp new file mode 100644 index 0000000..05ca34d --- /dev/null +++ b/libs/hwui/renderthread/EglManager.cpp @@ -0,0 +1,288 @@ +/* + * 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 "EglContext" + +#include "EglManager.h" + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "../RenderState.h" +#include "RenderThread.h" + +#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions" +#define GLES_VERSION 2 + +// Android-specific addition that is used to show when frames began in systrace +EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface); + +namespace android { +namespace uirenderer { +namespace renderthread { + +#define ERROR_CASE(x) case x: return #x; +static const char* egl_error_str(EGLint error) { + switch (error) { + ERROR_CASE(EGL_SUCCESS) + ERROR_CASE(EGL_NOT_INITIALIZED) + ERROR_CASE(EGL_BAD_ACCESS) + ERROR_CASE(EGL_BAD_ALLOC) + ERROR_CASE(EGL_BAD_ATTRIBUTE) + ERROR_CASE(EGL_BAD_CONFIG) + ERROR_CASE(EGL_BAD_CONTEXT) + ERROR_CASE(EGL_BAD_CURRENT_SURFACE) + ERROR_CASE(EGL_BAD_DISPLAY) + ERROR_CASE(EGL_BAD_MATCH) + ERROR_CASE(EGL_BAD_NATIVE_PIXMAP) + ERROR_CASE(EGL_BAD_NATIVE_WINDOW) + ERROR_CASE(EGL_BAD_PARAMETER) + ERROR_CASE(EGL_BAD_SURFACE) + ERROR_CASE(EGL_CONTEXT_LOST) + default: + return "Unknown error"; + } +} +static const char* egl_error_str() { + return egl_error_str(eglGetError()); +} + +static bool load_dirty_regions_property() { + char buf[PROPERTY_VALUE_MAX]; + int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true"); + return !strncasecmp("true", buf, len); +} + +EglManager::EglManager(RenderThread& thread) + : mRenderThread(thread) + , mEglDisplay(EGL_NO_DISPLAY) + , mEglConfig(0) + , mEglContext(EGL_NO_CONTEXT) + , mPBufferSurface(EGL_NO_SURFACE) + , mRequestDirtyRegions(load_dirty_regions_property()) + , mCurrentSurface(EGL_NO_SURFACE) + , mAtlasMap(NULL) + , mAtlasMapSize(0) { + mCanSetDirtyRegions = mRequestDirtyRegions; + ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); +} + +void EglManager::initialize() { + if (hasEglContext()) return; + + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); + + EGLint major, minor; + LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE, + "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str()); + + ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor); + + loadConfig(); + createContext(); + usePBufferSurface(); + mRenderThread.renderState().onGLContextCreated(); + initAtlas(); +} + +bool EglManager::hasEglContext() { + return mEglDisplay != EGL_NO_DISPLAY; +} + +void EglManager::requireGlContext() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context"); + + // We don't care *WHAT* surface is active, just that one is active to give + // us access to the GL context + if (mCurrentSurface == EGL_NO_SURFACE) { + usePBufferSurface(); + } +} + +void EglManager::loadConfig() { + EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint attribs[] = { + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 0, + EGL_CONFIG_CAVEAT, EGL_NONE, + EGL_STENCIL_SIZE, Stencil::getStencilSize(), + EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior, + EGL_NONE + }; + + EGLint num_configs = 1; + if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) + || num_configs != 1) { + // Failed to get a valid config + if (mCanSetDirtyRegions) { + ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); + // Try again without dirty regions enabled + mCanSetDirtyRegions = false; + loadConfig(); + } else { + LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); + } + } +} + +void EglManager::createContext() { + EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE }; + mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs); + LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT, + "Failed to create context, error = %s", egl_error_str()); +} + +void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, + int64_t* map, size_t mapSize) { + + // Already initialized + if (mAtlasBuffer.get()) { + ALOGW("Multiple calls to setTextureAtlas!"); + delete map; + return; + } + + mAtlasBuffer = buffer; + mAtlasMap = map; + mAtlasMapSize = mapSize; + + if (hasEglContext()) { + usePBufferSurface(); + initAtlas(); + } +} + +void EglManager::initAtlas() { + if (mAtlasBuffer.get()) { + Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + } +} + +void EglManager::usePBufferSurface() { + LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, + "usePBufferSurface() called on uninitialized GlobalContext!"); + + if (mPBufferSurface == EGL_NO_SURFACE) { + EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE }; + mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs); + } + makeCurrent(mPBufferSurface); +} + +EGLSurface EglManager::createSurface(EGLNativeWindowType window) { + initialize(); + EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL); + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Failed to create EGLSurface for window %p, eglErr = %s", + (void*) window, egl_error_str()); + return surface; +} + +void EglManager::destroySurface(EGLSurface surface) { + if (isCurrent(surface)) { + makeCurrent(EGL_NO_SURFACE); + } + if (!eglDestroySurface(mEglDisplay, surface)) { + ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str()); + } +} + +void EglManager::destroy() { + if (mEglDisplay == EGL_NO_DISPLAY) return; + + usePBufferSurface(); + if (Caches::hasInstance()) { + Caches::getInstance().terminate(); + } + + eglDestroyContext(mEglDisplay, mEglContext); + eglDestroySurface(mEglDisplay, mPBufferSurface); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + eglReleaseThread(); + + mEglDisplay = EGL_NO_DISPLAY; + mEglContext = EGL_NO_CONTEXT; + mPBufferSurface = EGL_NO_SURFACE; + mCurrentSurface = EGL_NO_SURFACE; +} + +bool EglManager::makeCurrent(EGLSurface surface) { + if (isCurrent(surface)) return false; + + if (surface == EGL_NO_SURFACE) { + // If we are setting EGL_NO_SURFACE we don't care about any of the potential + // return errors, which would only happen if mEglDisplay had already been + // destroyed in which case the current context is already NO_CONTEXT + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) { + LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s", + (void*)surface, egl_error_str()); + } + mCurrentSurface = surface; + return true; +} + +void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { + LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE, + "Tried to beginFrame on EGL_NO_SURFACE!"); + makeCurrent(surface); + if (width) { + eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width); + } + if (height) { + eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); + } + eglBeginFrame(mEglDisplay, surface); +} + +void EglManager::swapBuffers(EGLSurface surface) { + eglSwapBuffers(mEglDisplay, surface); + EGLint err = eglGetError(); + LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS, + "Encountered EGL error %d %s during rendering", err, egl_error_str(err)); +} + +bool EglManager::enableDirtyRegions(EGLSurface surface) { + if (!mRequestDirtyRegions) return false; + + if (mCanSetDirtyRegions) { + if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s", + (void*) surface, egl_error_str()); + return false; + } + return true; + } + // Perhaps it is already enabled? + EGLint value; + if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + return false; + } + return value == EGL_BUFFER_PRESERVED; +} + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h new file mode 100644 index 0000000..a844cfc --- /dev/null +++ b/libs/hwui/renderthread/EglManager.h @@ -0,0 +1,88 @@ +/* + * 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 EGLMANAGER_H +#define EGLMANAGER_H + +#include <cutils/compiler.h> +#include <EGL/egl.h> +#include <ui/GraphicBuffer.h> +#include <utils/StrongPointer.h> + +namespace android { +namespace uirenderer { +namespace renderthread { + +class RenderThread; + +// This class contains the shared global EGL objects, such as EGLDisplay +// and EGLConfig, which are re-used by CanvasContext +class EglManager { +public: + // Returns true on success, false on failure + void initialize(); + + bool hasEglContext(); + void requireGlContext(); + + void usePBufferSurface(); + EGLSurface createSurface(EGLNativeWindowType window); + void destroySurface(EGLSurface surface); + + void destroy(); + + bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; } + // Returns true if the current surface changed, false if it was already current + bool makeCurrent(EGLSurface surface); + void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); + void swapBuffers(EGLSurface surface); + + bool enableDirtyRegions(EGLSurface surface); + + void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); + +private: + friend class RenderThread; + + EglManager(RenderThread& thread); + // EglContext is never destroyed, method is purposely not implemented + ~EglManager(); + + void loadConfig(); + void createContext(); + void initAtlas(); + + RenderThread& mRenderThread; + + EGLDisplay mEglDisplay; + EGLConfig mEglConfig; + EGLContext mEglContext; + EGLSurface mPBufferSurface; + + const bool mRequestDirtyRegions; + bool mCanSetDirtyRegions; + + EGLSurface mCurrentSurface; + + sp<GraphicBuffer> mAtlasBuffer; + int64_t* mAtlasMap; + size_t mAtlasMapSize; +}; + +} /* namespace renderthread */ +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* EGLMANAGER_H */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 0901963..f90a26a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -47,13 +47,13 @@ namespace renderthread { #define SETUP_TASK(method) \ LOG_ALWAYS_FATAL_IF( METHOD_INVOKE_PAYLOAD_SIZE < sizeof(ARGS(method)), \ - "METHOD_INVOKE_PAYLOAD_SIZE %d is smaller than sizeof(" #method "Args) %d", \ + "METHOD_INVOKE_PAYLOAD_SIZE %zu is smaller than sizeof(" #method "Args) %zu", \ METHOD_INVOKE_PAYLOAD_SIZE, sizeof(ARGS(method))); \ MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \ ARGS(method) *args = (ARGS(method) *) task->payload() -CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) { - return new CanvasContext(args->translucent, args->rootRenderNode); +CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) { + return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode); } RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) @@ -62,6 +62,7 @@ RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode) SETUP_TASK(createContext); args->translucent = translucent; args->rootRenderNode = rootRenderNode; + args->thread = &mRenderThread; mContext = (CanvasContext*) postAndWait(task); mDrawFrameTask.setContext(&mRenderThread, mContext); } @@ -180,8 +181,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); } @@ -200,20 +200,29 @@ void RenderProxy::destroyCanvasAndSurface() { postAndWait(task); } -CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) { - args->context->invokeFunctor(args->functor); +CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) { + CanvasContext::invokeFunctor(*args->thread, args->functor); return NULL; } void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { ATRACE_CALL(); + RenderThread& thread = RenderThread::getInstance(); SETUP_TASK(invokeFunctor); - args->context = mContext; + args->thread = &thread; args->functor = functor; if (waitForCompletion) { - postAndWait(task); + // waitForCompletion = true is expected to be fairly rare and only + // happen in destruction. Thus it should be fine to temporarily + // create a Mutex + Mutex mutex; + Condition condition; + SignalingRenderTask syncTask(task, &mutex, &condition); + AutoMutex _lock(mutex); + thread.queue(&syncTask); + condition.wait(mutex); } else { - post(task); + thread.queue(task); } } @@ -234,7 +243,7 @@ CREATE_BRIDGE1(destroyLayer, Layer* layer) { return NULL; } -static void enqueueDestroyLayer(Layer* layer) { +void RenderProxy::enqueueDestroyLayer(Layer* layer) { SETUP_TASK(destroyLayer); args->layer = layer; RenderThread::getInstance().queue(task); @@ -243,7 +252,7 @@ static void enqueueDestroyLayer(Layer* layer) { CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) { Layer* layer = args->context->createRenderLayer(args->width, args->height); if (!layer) return 0; - return new DeferredLayerUpdater(layer, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) { @@ -259,7 +268,7 @@ DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; - return new DeferredLayerUpdater(layer, enqueueDestroyLayer); + return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer); } DeferredLayerUpdater* RenderProxy::createTextureLayer() { @@ -337,6 +346,22 @@ void RenderProxy::dumpProfileInfo(int fd) { postAndWait(task); } +CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) { + CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size); + args->buffer->decStrong(0); + return NULL; +} + +void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) { + SETUP_TASK(setTextureAtlas); + args->thread = &mRenderThread; + args->buffer = buffer.get(); + args->buffer->incStrong(0); + args->map = map; + args->size = size; + post(task); +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 944ff9c..df0aff0 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -70,13 +70,14 @@ 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); + ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion); ANDROID_API void runWithGlContext(RenderTask* task); + static void enqueueDestroyLayer(Layer* layer); ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -90,6 +91,8 @@ public: ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); + private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 4a4e254..03e98d5 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -21,7 +21,9 @@ #include <gui/DisplayEventReceiver.h> #include <utils/Log.h> +#include "../RenderState.h" #include "CanvasContext.h" +#include "EglManager.h" #include "RenderProxy.h" namespace android { @@ -138,13 +140,16 @@ RenderThread::RenderThread() : Thread(true), Singleton<RenderThread>() , mDisplayEventReceiver(0) , mVsyncRequested(false) , mFrameCallbackTaskPending(false) - , mFrameCallbackTask(0) { + , mFrameCallbackTask(0) + , mRenderState(NULL) + , mEglManager(NULL) { mFrameCallbackTask = new DispatchFrameCallbacks(this); mLooper = new Looper(false); run("RenderThread"); } RenderThread::~RenderThread() { + LOG_ALWAYS_FATAL("Can't destroy the render thread"); } void RenderThread::initializeDisplayEventReceiver() { @@ -159,6 +164,12 @@ void RenderThread::initializeDisplayEventReceiver() { Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this); } +void RenderThread::initThreadLocals() { + initializeDisplayEventReceiver(); + mEglManager = new EglManager(*this); + mRenderState = new RenderState(); +} + int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) { ALOGE("Display event receiver pipe was closed or an error occurred. " @@ -233,7 +244,7 @@ void RenderThread::requestVsync() { } bool RenderThread::threadLoop() { - initializeDisplayEventReceiver(); + initThreadLocals(); int timeoutMillis = -1; for (;;) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index 4412584..0b91e9d 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -31,12 +31,18 @@ #include "TimeLord.h" namespace android { + class DisplayEventReceiver; namespace uirenderer { + +class RenderState; + namespace renderthread { class DispatchFrameCallbacks; +class EglManager; +class RenderProxy; class TaskQueue { public: @@ -62,7 +68,7 @@ protected: ~IFrameCallback() {} }; -class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> { +class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> { public: // RenderThread takes complete ownership of tasks that are queued // and will delete them after they are run @@ -79,6 +85,8 @@ public: void pushBackFrameCallback(IFrameCallback* callback); TimeLord& timeLord() { return mTimeLord; } + RenderState& renderState() { return *mRenderState; } + EglManager& eglManager() { return *mEglManager; } protected: virtual bool threadLoop(); @@ -86,10 +94,12 @@ protected: private: friend class Singleton<RenderThread>; friend class DispatchFrameCallbacks; + friend class RenderProxy; RenderThread(); virtual ~RenderThread(); + void initThreadLocals(); void initializeDisplayEventReceiver(); static int displayEventReceiverCallback(int fd, int events, void* data); void drainDisplayEventQueue(bool skipCallbacks = false); @@ -119,6 +129,8 @@ private: DispatchFrameCallbacks* mFrameCallbackTask; TimeLord mTimeLord; + RenderState* mRenderState; + EglManager* mEglManager; }; } /* namespace renderthread */ diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h index 9a211a2..30b6ff2 100644 --- a/libs/hwui/thread/Task.h +++ b/libs/hwui/thread/Task.h @@ -17,8 +17,6 @@ #ifndef ANDROID_HWUI_TASK_H #define ANDROID_HWUI_TASK_H -#define ATRACE_TAG ATRACE_TAG_VIEW - #include <utils/RefBase.h> #include <utils/Trace.h> @@ -40,7 +38,7 @@ public: virtual ~Task() { } T getResult() const { - ATRACE_NAME("waitForTask"); + ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask"); return mFuture->get(); } diff --git a/libs/hwui/utils/Blur.cpp b/libs/hwui/utils/Blur.cpp index c020b40..877a422 100644 --- a/libs/hwui/utils/Blur.cpp +++ b/libs/hwui/utils/Blur.cpp @@ -19,6 +19,7 @@ #include <math.h> #include "Blur.h" +#include "MathUtils.h" namespace android { namespace uirenderer { @@ -35,6 +36,17 @@ float Blur::convertSigmaToRadius(float sigma) { return sigma > 0.5f ? (sigma - 0.5f) / BLUR_SIGMA_SCALE : 0.0f; } +// if the original radius was on an integer boundary and the resulting radius +// is within the conversion error tolerance then we attempt to snap to the +// original integer boundary. +uint32_t Blur::convertRadiusToInt(float radius) { + const float radiusCeil = ceilf(radius); + if (MathUtils::areEqual(radiusCeil, radius)) { + return radiusCeil; + } + return radius; +} + /** * HWUI has used a slightly different equation than Skia to generate the value * for sigma and to preserve compatibility we have kept that logic. diff --git a/libs/hwui/utils/Blur.h b/libs/hwui/utils/Blur.h index 79aff65..b145333 100644 --- a/libs/hwui/utils/Blur.h +++ b/libs/hwui/utils/Blur.h @@ -27,8 +27,12 @@ class Blur { public: // If radius > 0, return the corresponding sigma, else return 0 ANDROID_API static float convertRadiusToSigma(float radius); - // If sigma > 0.6, return the corresponding radius, else return 0 + // If sigma > 0.5, return the corresponding radius, else return 0 ANDROID_API static float convertSigmaToRadius(float sigma); + // If the original radius was on an integer boundary then after the sigma to + // radius conversion a small rounding error may be introduced. This function + // accounts for that error and snaps to the appropriate integer boundary. + static uint32_t convertRadiusToInt(float radius); static void generateGaussianWeights(float* weights, int32_t radius); static void horizontal(float* weights, int32_t radius, const uint8_t* source, |