diff options
Diffstat (limited to 'libs/hwui')
62 files changed, 1260 insertions, 1379 deletions
diff --git a/libs/hwui/AmbientShadow.cpp b/libs/hwui/AmbientShadow.cpp index f11437b..5840107 100644 --- a/libs/hwui/AmbientShadow.cpp +++ b/libs/hwui/AmbientShadow.cpp @@ -325,10 +325,11 @@ void AmbientShadow::createAmbientShadow(bool isCasterOpaque, // At the end, update the real index and vertex buffer size. shadowVertexBuffer.updateVertexCount(vertexBufferIndex); shadowVertexBuffer.updateIndexCount(indexBufferIndex); + shadowVertexBuffer.computeBounds<AlphaVertex>(); - ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Vertex Buffer"); - ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Index Buffer"); - ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Umbra Buffer"); + ShadowTessellator::checkOverflow(vertexBufferIndex, totalVertexCount, "Ambient Vertex Buffer"); + ShadowTessellator::checkOverflow(indexBufferIndex, totalIndexCount, "Ambient Index Buffer"); + ShadowTessellator::checkOverflow(umbraIndex, totalUmbraCount, "Ambient Umbra Buffer"); #if DEBUG_SHADOW for (int i = 0; i < vertexBufferIndex; i++) { diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 3e590a9..d0b9d82 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -6,8 +6,6 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk # getConfig in external/skia/include/core/SkBitmap.h is deprecated. # Allow Gnu extension: in-class initializer of static 'const float' member. LOCAL_CLANG_CFLAGS += \ - -Wno-unused-parameter \ - -Wno-deprecated-declarations \ -Wno-gnu-static-float-init # Only build libhwui when USE_OPENGL_RENDERER is diff --git a/libs/hwui/AssetAtlas.cpp b/libs/hwui/AssetAtlas.cpp index fc86e4f..52ca92d 100644 --- a/libs/hwui/AssetAtlas.cpp +++ b/libs/hwui/AssetAtlas.cpp @@ -33,40 +33,46 @@ void AssetAtlas::init(sp<GraphicBuffer> buffer, int64_t* map, int count) { return; } - mImage = new Image(buffer); + ATRACE_NAME("AssetAtlas::init"); + mImage = new Image(buffer); if (mImage->getTexture()) { - Caches& caches = Caches::getInstance(); - - mTexture = new Texture(caches); - mTexture->id = mImage->getTexture(); - mTexture->width = buffer->getWidth(); - mTexture->height = buffer->getHeight(); - - createEntries(caches, map, count); + if (!mTexture) { + Caches& caches = Caches::getInstance(); + mTexture = new Texture(caches); + mTexture->width = buffer->getWidth(); + mTexture->height = buffer->getHeight(); + createEntries(caches, map, count); + } } else { ALOGW("Could not create atlas image"); - delete mImage; mImage = NULL; - mTexture = NULL; } - mGenerationId++; + updateTextureId(); } void AssetAtlas::terminate() { if (mImage) { delete mImage; mImage = NULL; + updateTextureId(); + } +} - delete mTexture; - mTexture = NULL; - for (size_t i = 0; i < mEntries.size(); i++) { - delete mEntries.valueAt(i); - } - mEntries.clear(); +void AssetAtlas::updateTextureId() { + mTexture->id = mImage ? mImage->getTexture() : 0; + if (mTexture->id) { + // Texture ID changed, force-set to defaults to sync the wrapper & GL + // state objects + mTexture->setWrap(GL_CLAMP_TO_EDGE, false, true); + mTexture->setFilter(GL_NEAREST, false, true); + } + for (size_t i = 0; i < mEntries.size(); i++) { + AssetAtlas::Entry* entry = mEntries.valueAt(i); + entry->texture->id = mTexture->id; } } @@ -131,7 +137,6 @@ void AssetAtlas::createEntries(Caches& caches, int64_t* map, int count) { y / height, (y + bitmap->height()) / height); Texture* texture = new DelegateTexture(caches, mTexture); - texture->id = mTexture->id; texture->blend = !bitmap->isOpaque(); texture->width = bitmap->width(); texture->height = bitmap->height(); diff --git a/libs/hwui/AssetAtlas.h b/libs/hwui/AssetAtlas.h index 2ec556e..fffd740 100644 --- a/libs/hwui/AssetAtlas.h +++ b/libs/hwui/AssetAtlas.h @@ -106,7 +106,7 @@ public: friend class AssetAtlas; }; - AssetAtlas(): mTexture(NULL), mImage(NULL), mGenerationId(0), + AssetAtlas(): mTexture(NULL), mImage(NULL), mBlendKey(true), mOpaqueKey(false) { } ~AssetAtlas() { terminate(); } @@ -130,7 +130,7 @@ public: * After calling this method, the width, height * and texture are set to 0. */ - ANDROID_API void terminate(); + void terminate(); /** * Returns the width of this atlas in pixels. @@ -168,21 +168,13 @@ public: */ Texture* getEntryTexture(const SkBitmap* bitmap) const; - /** - * Returns the current generation id of the atlas. - */ - uint32_t getGenerationId() const { - return mGenerationId; - } - private: void createEntries(Caches& caches, int64_t* map, int count); + void updateTextureId(); Texture* mTexture; Image* mImage; - uint32_t mGenerationId; - const bool mBlendKey; const bool mOpaqueKey; diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index f0bf7b2..4bbe6ed 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -66,6 +66,8 @@ Caches::Caches(): Singleton<Caches>(), bool Caches::init() { if (mInitialized) return false; + ATRACE_NAME("Caches::init"); + glGenBuffers(1, &meshBuffer); glBindBuffer(GL_ARRAY_BUFFER, meshBuffer); glBufferData(GL_ARRAY_BUFFER, sizeof(gMeshVertices), gMeshVertices, GL_STATIC_DRAW); @@ -235,8 +237,6 @@ void Caches::terminate() { programCache.clear(); currentProgram = NULL; - assetAtlas.terminate(); - patchCache.clear(); clearGarbage(); @@ -265,14 +265,27 @@ void Caches::dumpMemoryUsage() { } void Caches::dumpMemoryUsage(String8 &log) { + uint32_t total = 0; log.appendFormat("Current memory usage / total memory usage (bytes):\n"); log.appendFormat(" TextureCache %8d / %8d\n", textureCache.getSize(), textureCache.getMaxSize()); log.appendFormat(" LayerCache %8d / %8d (numLayers = %zu)\n", layerCache.getSize(), layerCache.getMaxSize(), layerCache.getCount()); - log.appendFormat(" Garbage layers %8zu\n", mLayerGarbage.size()); - log.appendFormat(" Active layers %8zu\n", - mRenderState ? mRenderState->mActiveLayers.size() : 0); + if (mRenderState) { + int memused = 0; + for (std::set<const Layer*>::iterator it = mRenderState->mActiveLayers.begin(); + it != mRenderState->mActiveLayers.end(); it++) { + const Layer* layer = *it; + log.appendFormat(" Layer size %dx%d; isTextureLayer()=%d; texid=%u fbo=%u; refs=%d\n", + layer->getWidth(), layer->getHeight(), + layer->isTextureLayer(), layer->getTexture(), + layer->getFbo(), layer->getStrongCount()); + memused += layer->getWidth() * layer->getHeight() * 4; + } + log.appendFormat(" Layers total %8d (numLayers = %zu)\n", + memused, mRenderState->mActiveLayers.size()); + total += memused; + } log.appendFormat(" RenderBufferCache %8d / %8d\n", renderBufferCache.getSize(), renderBufferCache.getMaxSize()); log.appendFormat(" GradientCache %8d / %8d\n", @@ -297,9 +310,7 @@ void Caches::dumpMemoryUsage(String8 &log) { log.appendFormat(" FboCache %8d / %8d\n", fboCache.getSize(), fboCache.getMaxSize()); - uint32_t total = 0; total += textureCache.getSize(); - total += layerCache.getSize(); total += renderBufferCache.getSize(); total += gradientCache.getSize(); total += pathCache.getSize(); @@ -323,27 +334,6 @@ void Caches::clearGarbage() { textureCache.clearGarbage(); pathCache.clearGarbage(); patchCache.clearGarbage(); - - Vector<Layer*> layers; - - { // scope for the lock - Mutex::Autolock _l(mGarbageLock); - layers = mLayerGarbage; - mLayerGarbage.clear(); - } - - size_t count = layers.size(); - for (size_t i = 0; i < count; i++) { - Layer* layer = layers.itemAt(i); - delete layer; - } - layers.clear(); -} - -void Caches::deleteLayerDeferred(Layer* layer) { - Mutex::Autolock _l(mGarbageLock); - layer->state = Layer::kState_InGarbageList; - mLayerGarbage.push(layer); } void Caches::flush(FlushMode mode) { diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index 7aa628c..2e179af 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -340,7 +340,6 @@ public: TessellationCache tessellationCache; TextDropShadowCache dropShadowCache; FboCache fboCache; - ResourceCache resourceCache; GammaFontRenderer* fontRenderer; @@ -349,8 +348,6 @@ public: Dither dither; Stencil stencil; - AssetAtlas assetAtlas; - bool gpuPixelBuffersEnabled; // Debug methods diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index fbe2d39..6fd0151 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -332,8 +332,8 @@ private: class RestoreToCountBatch : public Batch { public: - RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, int restoreCount) : - mState(state), mRestoreCount(restoreCount) {} + RestoreToCountBatch(const StateOp* op, const DeferredDisplayState* state, + int restoreCount) : mState(state), mRestoreCount(restoreCount) {} virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty, int index) { DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount); diff --git a/libs/hwui/DeferredLayerUpdater.cpp b/libs/hwui/DeferredLayerUpdater.cpp index a6d7e78..d02455c 100644 --- a/libs/hwui/DeferredLayerUpdater.cpp +++ b/libs/hwui/DeferredLayerUpdater.cpp @@ -24,25 +24,6 @@ namespace android { namespace uirenderer { -class DeleteLayerTask : public renderthread::RenderTask { -public: - DeleteLayerTask(renderthread::EglManager& eglManager, Layer* layer) - : mEglManager(eglManager) - , mLayer(layer) - {} - - virtual void run() { - mEglManager.requireGlContext(); - LayerRenderer::destroyLayer(mLayer); - mLayer = 0; - delete this; - } - -private: - renderthread::EglManager& mEglManager; - Layer* mLayer; -}; - DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, Layer* layer) : mSurfaceTexture(0) , mTransform(0) @@ -62,7 +43,7 @@ DeferredLayerUpdater::DeferredLayerUpdater(renderthread::RenderThread& thread, L DeferredLayerUpdater::~DeferredLayerUpdater() { SkSafeUnref(mColorFilter); setTransform(0); - mRenderThread.queue(new DeleteLayerTask(mRenderThread.eglManager(), mLayer)); + mLayer->postDecStrong(); mLayer = 0; } diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h index dda3e89..84411ed 100644 --- a/libs/hwui/DeferredLayerUpdater.h +++ b/libs/hwui/DeferredLayerUpdater.h @@ -76,7 +76,7 @@ public: ANDROID_API bool apply(); - ANDROID_API Layer* backingLayer() { + Layer* backingLayer() { return mLayer; } diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index d8932ce..8953166 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -39,33 +39,28 @@ DisplayListData::~DisplayListData() { } void DisplayListData::cleanupResources() { - Caches& caches = Caches::getInstance(); - caches.unregisterFunctors(functors.size()); - caches.resourceCache.lock(); + ResourceCache& resourceCache = ResourceCache::getInstance(); + resourceCache.lock(); for (size_t i = 0; i < bitmapResources.size(); i++) { - caches.resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i)); + resourceCache.decrementRefcountLocked(bitmapResources.itemAt(i)); } for (size_t i = 0; i < ownedBitmapResources.size(); i++) { const SkBitmap* bitmap = ownedBitmapResources.itemAt(i); - caches.resourceCache.decrementRefcountLocked(bitmap); - caches.resourceCache.destructorLocked(bitmap); + resourceCache.decrementRefcountLocked(bitmap); + resourceCache.destructorLocked(bitmap); } for (size_t i = 0; i < patchResources.size(); i++) { - caches.resourceCache.decrementRefcountLocked(patchResources.itemAt(i)); + resourceCache.decrementRefcountLocked(patchResources.itemAt(i)); } for (size_t i = 0; i < sourcePaths.size(); i++) { - caches.resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i)); + resourceCache.decrementRefcountLocked(sourcePaths.itemAt(i)); } - for (size_t i = 0; i < layers.size(); i++) { - caches.resourceCache.decrementRefcountLocked(layers.itemAt(i)); - } - - caches.resourceCache.unlock(); + resourceCache.unlock(); for (size_t i = 0; i < paints.size(); i++) { delete paints.itemAt(i); @@ -86,7 +81,6 @@ void DisplayListData::cleanupResources() { paints.clear(); regions.clear(); paths.clear(); - layers.clear(); } size_t DisplayListData::addChild(DrawRenderNodeOp* op) { diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h index 9eb8ae3..a9a9148 100644 --- a/libs/hwui/DisplayList.h +++ b/libs/hwui/DisplayList.h @@ -77,18 +77,14 @@ public: OpenGLRenderer& mRenderer; const int mReplayFlags; - // Allocator with the lifetime of a single frame. - // replay uses an Allocator owned by the struct, while defer shares the DeferredDisplayList's Allocator + // Allocator with the lifetime of a single frame. replay uses an Allocator owned by the struct, + // while defer shares the DeferredDisplayList's Allocator + // TODO: move this allocator to be owned by object with clear frame lifecycle LinearAllocator * const mAllocator; SkPath* allocPathForFrame() { - mTempPaths.push_back(SkPath()); - return &mTempPaths.back(); + return mRenderer.allocPathForFrame(); } - -private: - // Paths kept alive for the duration of the frame - std::vector<SkPath> mTempPaths; }; struct DeferStateStruct : public PlaybackStateStruct { @@ -146,7 +142,6 @@ public: Vector<const SkPath*> paths; SortedVector<const SkPath*> sourcePaths; Vector<const SkRegion*> regions; - Vector<Layer*> layers; Vector<Functor*> functors; const Vector<Chunk>& getChunks() const { @@ -156,11 +151,7 @@ public: size_t addChild(DrawRenderNodeOp* childOp); const Vector<DrawRenderNodeOp*>& children() { return mChildren; } - void refProperty(CanvasPropertyPrimitive* prop) { - mReferenceHolders.push(prop); - } - - void refProperty(CanvasPropertyPaint* prop) { + void ref(VirtualLightRefBase* prop) { mReferenceHolders.push(prop); } diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h index cb3ef9b..8a5e21d 100644 --- a/libs/hwui/DisplayListOp.h +++ b/libs/hwui/DisplayListOp.h @@ -32,6 +32,7 @@ #include "AssetAtlas.h" #include "DeferredDisplayList.h" #include "DisplayListRenderer.h" +#include "RenderState.h" #include "UvMapper.h" #include "utils/LinearAllocator.h" @@ -212,10 +213,13 @@ protected: // check state/paint for transparency if (mPaint) { + if (mPaint->getAlpha() != 0xFF) { + return false; + } if (mPaint->getShader() && !mPaint->getShader()->isOpaque()) { return false; } - if (mPaint->getAlpha() != 0xFF) { + if (Renderer::isBlendedColorFilter(mPaint->getColorFilter())) { return false; } } @@ -644,24 +648,17 @@ public: DrawBitmapOp(const SkBitmap* bitmap, const SkPaint* paint) : DrawBoundedOp(0, 0, bitmap->width(), bitmap->height(), paint) , mBitmap(bitmap) - , mAtlas(Caches::getInstance().assetAtlas) { - mEntry = mAtlas.getEntry(bitmap); - if (mEntry) { - mEntryGenerationId = mAtlas.getGenerationId(); - mUvMapper = mEntry->uvMapper; - } + , mEntryValid(false), mEntry(NULL) { } virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { return renderer.drawBitmap(mBitmap, getPaint(renderer)); } - AssetAtlas::Entry* getAtlasEntry() { - // The atlas entry is stale, let's get a new one - if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) { - mEntryGenerationId = mAtlas.getGenerationId(); - mEntry = mAtlas.getEntry(mBitmap); - mUvMapper = mEntry->uvMapper; + AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { + if (!mEntryValid) { + mEntryValid = true; + mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap); } return mEntry; } @@ -697,7 +694,7 @@ public: pureTranslate &= state.mMatrix.isPureTranslate(); Rect texCoords(0, 0, 1, 1); - ((DrawBitmapOp*) ops[i].op)->mUvMapper.map(texCoords); + ((DrawBitmapOp*) ops[i].op)->uvMap(renderer, texCoords); SET_TEXTURE(vertex, opBounds, bounds, texCoords, left, top); SET_TEXTURE(vertex, opBounds, bounds, texCoords, right, top); @@ -717,7 +714,8 @@ public: } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw bitmap %p at %f %f", mBitmap, mLocalBounds.left, mLocalBounds.top); + OP_LOG("Draw bitmap %p at %f %f%s", mBitmap, mLocalBounds.left, mLocalBounds.top, + mEntry ? " using AssetAtlas" : ""); } virtual const char* name() { return "DrawBitmap"; } @@ -725,7 +723,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Bitmap; - deferInfo.mergeId = getAtlasEntry() ? + deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation @@ -738,13 +736,17 @@ public: (mBitmap->colorType() != kAlpha_8_SkColorType); } + void uvMap(OpenGLRenderer& renderer, Rect& texCoords) { + if (getAtlasEntry(renderer)) { + mEntry->uvMapper.map(texCoords); + } + } + const SkBitmap* bitmap() { return mBitmap; } protected: const SkBitmap* mBitmap; - const AssetAtlas& mAtlas; - uint32_t mEntryGenerationId; + bool mEntryValid; AssetAtlas::Entry* mEntry; - UvMapper mUvMapper; }; class DrawBitmapRectOp : public DrawBoundedOp { @@ -837,18 +839,13 @@ public: float left, float top, float right, float bottom, const SkPaint* paint) : DrawBoundedOp(left, top, right, bottom, paint), mBitmap(bitmap), mPatch(patch), mGenerationId(0), mMesh(NULL), - mAtlas(Caches::getInstance().assetAtlas) { - mEntry = mAtlas.getEntry(bitmap); - if (mEntry) { - mEntryGenerationId = mAtlas.getGenerationId(); - } + mEntryValid(false), mEntry(NULL) { }; - AssetAtlas::Entry* getAtlasEntry() { - // The atlas entry is stale, let's get a new one - if (mEntry && mEntryGenerationId != mAtlas.getGenerationId()) { - mEntryGenerationId = mAtlas.getGenerationId(); - mEntry = mAtlas.getEntry(mBitmap); + AssetAtlas::Entry* getAtlasEntry(OpenGLRenderer& renderer) { + if (!mEntryValid) { + mEntryValid = true; + mEntry = renderer.renderState().assetAtlas().getEntry(mBitmap); } return mEntry; } @@ -856,7 +853,7 @@ public: const Patch* getMesh(OpenGLRenderer& renderer) { if (!mMesh || renderer.getCaches().patchCache.getGenerationId() != mGenerationId) { PatchCache& cache = renderer.getCaches().patchCache; - mMesh = cache.get(getAtlasEntry(), mBitmap->width(), mBitmap->height(), + mMesh = cache.get(getAtlasEntry(renderer), mBitmap->width(), mBitmap->height(), mLocalBounds.getWidth(), mLocalBounds.getHeight(), mPatch); mGenerationId = cache.getGenerationId(); } @@ -938,20 +935,21 @@ public: indexCount += opMesh->indexCount; } - return renderer.drawPatches(mBitmap, getAtlasEntry(), + return renderer.drawPatches(mBitmap, getAtlasEntry(renderer), &vertices[0], indexCount, getPaint(renderer)); } virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) { // We're not calling the public variant of drawPatch() here // This method won't perform the quickReject() since we've already done it at this point - return renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(), + return renderer.drawPatch(mBitmap, getMesh(renderer), getAtlasEntry(renderer), mLocalBounds.left, mLocalBounds.top, mLocalBounds.right, mLocalBounds.bottom, getPaint(renderer)); } virtual void output(int level, uint32_t logFlags) const { - OP_LOG("Draw patch " RECT_STRING, RECT_ARGS(mLocalBounds)); + OP_LOG("Draw patch " RECT_STRING "%s", RECT_ARGS(mLocalBounds), + mEntry ? " with AssetAtlas" : ""); } virtual const char* name() { return "DrawPatch"; } @@ -959,7 +957,7 @@ public: virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo, const DeferredDisplayState& state) { deferInfo.batchId = DeferredDisplayList::kOpBatch_Patch; - deferInfo.mergeId = getAtlasEntry() ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; + deferInfo.mergeId = getAtlasEntry(renderer) ? (mergeid_t) mEntry->getMergeId() : (mergeid_t) mBitmap; deferInfo.mergeable = state.mMatrix.isPureTranslate() && OpenGLRenderer::getXfermodeDirect(mPaint) == SkXfermode::kSrcOver_Mode; deferInfo.opaqueOverBounds = isOpaqueOverBounds(state) && mBitmap->isOpaque(); @@ -972,8 +970,7 @@ private: uint32_t mGenerationId; const Patch* mMesh; - const AssetAtlas& mAtlas; - uint32_t mEntryGenerationId; + bool mEntryValid; AssetAtlas::Entry* mEntry; }; diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 1f70921..42ac07e 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -21,8 +21,9 @@ #include <private/hwui/DrawGlInfo.h> -#include "Caches.h" +#include "ResourceCache.h" #include "DeferredDisplayList.h" +#include "DeferredLayerUpdater.h" #include "DisplayListLogBuffer.h" #include "DisplayListOp.h" #include "DisplayListRenderer.h" @@ -32,7 +33,7 @@ namespace android { namespace uirenderer { DisplayListRenderer::DisplayListRenderer() - : mCaches(Caches::getInstance()) + : mResourceCache(ResourceCache::getInstance()) , mDisplayListData(NULL) , mTranslateX(0.0f) , mTranslateY(0.0f) @@ -177,7 +178,8 @@ bool DisplayListRenderer::clipRegion(const SkRegion* region, SkRegion::Op op) { return StatefulBaseRenderer::clipRegion(region, op); } -status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) { +status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, + int32_t flags) { LOG_ALWAYS_FATAL_IF(!renderNode, "missing rendernode"); // dirty is an out parameter and should not be recorded, @@ -188,9 +190,11 @@ status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty return DrawGlInfo::kStatusDone; } -status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) { - layer = refLayer(layer); - addDrawOp(new (alloc()) DrawLayerOp(layer, x, y)); +status_t DisplayListRenderer::drawLayer(DeferredLayerUpdater* layerHandle, float x, float y) { + // We ref the DeferredLayerUpdater due to its thread-safe ref-counting + // semantics. + mDisplayListData->ref(layerHandle); + addDrawOp(new (alloc()) DrawLayerOp(layerHandle->backingLayer(), x, y)); return DrawGlInfo::kStatusDone; } @@ -280,13 +284,13 @@ status_t DisplayListRenderer::drawRoundRect( CanvasPropertyPrimitive* right, CanvasPropertyPrimitive* bottom, CanvasPropertyPrimitive* rx, CanvasPropertyPrimitive* ry, CanvasPropertyPaint* paint) { - mDisplayListData->refProperty(left); - mDisplayListData->refProperty(top); - mDisplayListData->refProperty(right); - mDisplayListData->refProperty(bottom); - mDisplayListData->refProperty(rx); - mDisplayListData->refProperty(ry); - mDisplayListData->refProperty(paint); + mDisplayListData->ref(left); + mDisplayListData->ref(top); + mDisplayListData->ref(right); + mDisplayListData->ref(bottom); + mDisplayListData->ref(rx); + mDisplayListData->ref(ry); + mDisplayListData->ref(paint); addDrawOp(new (alloc()) DrawRoundRectPropsOp(&left->value, &top->value, &right->value, &bottom->value, &rx->value, &ry->value, &paint->value)); return DrawGlInfo::kStatusDone; @@ -300,10 +304,10 @@ status_t DisplayListRenderer::drawCircle(float x, float y, float radius, const S status_t DisplayListRenderer::drawCircle(CanvasPropertyPrimitive* x, CanvasPropertyPrimitive* y, CanvasPropertyPrimitive* radius, CanvasPropertyPaint* paint) { - mDisplayListData->refProperty(x); - mDisplayListData->refProperty(y); - mDisplayListData->refProperty(radius); - mDisplayListData->refProperty(paint); + mDisplayListData->ref(x); + mDisplayListData->ref(y); + mDisplayListData->ref(radius); + mDisplayListData->ref(paint); addDrawOp(new (alloc()) DrawCirclePropsOp(&x->value, &y->value, &radius->value, &paint->value)); return DrawGlInfo::kStatusDone; diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 3a3fc3a..2cc2be3 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -24,6 +24,7 @@ #include "DisplayListLogBuffer.h" #include "RenderNode.h" +#include "ResourceCache.h" namespace android { namespace uirenderer { @@ -44,6 +45,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// class DeferredDisplayList; +class DeferredLayerUpdater; class DisplayListRenderer; class DisplayListOp; class DrawOp; @@ -150,7 +152,7 @@ public: // ---------------------------------------------------------------------------- // Canvas draw operations - special // ---------------------------------------------------------------------------- - virtual status_t drawLayer(Layer* layer, float x, float y); + virtual status_t drawLayer(DeferredLayerUpdater* layerHandle, float x, float y); virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags); // TODO: rename for consistency @@ -209,7 +211,7 @@ private: mDisplayListData->paths.add(pathCopy); } if (mDisplayListData->sourcePaths.indexOf(path) < 0) { - mCaches.resourceCache.incrementRefcount(path); + mResourceCache.incrementRefcount(path); mDisplayListData->sourcePaths.add(path); } return pathCopy; @@ -267,31 +269,25 @@ private: return regionCopy; } - inline Layer* refLayer(Layer* layer) { - mDisplayListData->layers.add(layer); - mCaches.resourceCache.incrementRefcount(layer); - return layer; - } - inline const SkBitmap* refBitmap(const SkBitmap* bitmap) { // Note that this assumes the bitmap is immutable. There are cases this won't handle // correctly, such as creating the bitmap from scratch, drawing with it, changing its // contents, and drawing again. The only fix would be to always copy it the first time, // which doesn't seem worth the extra cycles for this unlikely case. mDisplayListData->bitmapResources.add(bitmap); - mCaches.resourceCache.incrementRefcount(bitmap); + mResourceCache.incrementRefcount(bitmap); return bitmap; } inline const SkBitmap* refBitmapData(const SkBitmap* bitmap) { mDisplayListData->ownedBitmapResources.add(bitmap); - mCaches.resourceCache.incrementRefcount(bitmap); + mResourceCache.incrementRefcount(bitmap); return bitmap; } inline const Res_png_9patch* refPatch(const Res_png_9patch* patch) { mDisplayListData->patchResources.add(patch); - mCaches.resourceCache.incrementRefcount(patch); + mResourceCache.incrementRefcount(patch); return patch; } @@ -299,7 +295,7 @@ private: DefaultKeyedVector<const SkPath*, const SkPath*> mPathMap; DefaultKeyedVector<const SkRegion*, const SkRegion*> mRegionMap; - Caches& mCaches; + ResourceCache& mResourceCache; DisplayListData* mDisplayListData; float mTranslateX; diff --git a/libs/hwui/DrawProfiler.cpp b/libs/hwui/DrawProfiler.cpp index 2409554..e590642 100644 --- a/libs/hwui/DrawProfiler.cpp +++ b/libs/hwui/DrawProfiler.cpp @@ -22,7 +22,8 @@ #define DEFAULT_MAX_FRAMES 128 -#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone)) return +#define RETURN_IF_PROFILING_DISABLED() if (CC_LIKELY(mType == kNone)) return +#define RETURN_IF_DISABLED() if (CC_LIKELY(mType == kNone && !mShowDirtyRegions)) return #define NANOS_TO_MILLIS_FLOAT(nanos) ((nanos) * 0.000001f) @@ -64,7 +65,9 @@ DrawProfiler::DrawProfiler() , mPreviousTime(0) , mVerticalUnit(0) , mHorizontalUnit(0) - , mThresholdStroke(0) { + , mThresholdStroke(0) + , mShowDirtyRegions(false) + , mFlashToggle(false) { setDensity(1); } @@ -82,27 +85,27 @@ void DrawProfiler::setDensity(float density) { } void DrawProfiler::startFrame(nsecs_t recordDurationNanos) { - RETURN_IF_DISABLED(); + RETURN_IF_PROFILING_DISABLED(); mData[mCurrentFrame].record = NANOS_TO_MILLIS_FLOAT(recordDurationNanos); mPreviousTime = systemTime(CLOCK_MONOTONIC); } void DrawProfiler::markPlaybackStart() { - RETURN_IF_DISABLED(); + RETURN_IF_PROFILING_DISABLED(); nsecs_t now = systemTime(CLOCK_MONOTONIC); mData[mCurrentFrame].prepare = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); mPreviousTime = now; } void DrawProfiler::markPlaybackEnd() { - RETURN_IF_DISABLED(); + RETURN_IF_PROFILING_DISABLED(); nsecs_t now = systemTime(CLOCK_MONOTONIC); mData[mCurrentFrame].playback = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); mPreviousTime = now; } void DrawProfiler::finishFrame() { - RETURN_IF_DISABLED(); + RETURN_IF_PROFILING_DISABLED(); nsecs_t now = systemTime(CLOCK_MONOTONIC); mData[mCurrentFrame].swapBuffers = NANOS_TO_MILLIS_FLOAT(now - mPreviousTime); mPreviousTime = now; @@ -114,19 +117,30 @@ void DrawProfiler::unionDirty(SkRect* dirty) { // Not worth worrying about minimizing the dirty region for debugging, so just // dirty the entire viewport. if (dirty) { + mDirtyRegion = *dirty; dirty->setEmpty(); } } void DrawProfiler::draw(OpenGLRenderer* canvas) { - if (CC_LIKELY(mType != kBars)) { - return; + RETURN_IF_DISABLED(); + + if (mShowDirtyRegions) { + mFlashToggle = !mFlashToggle; + if (mFlashToggle) { + SkPaint paint; + paint.setColor(0x7fff0000); + canvas->drawRect(mDirtyRegion.fLeft, mDirtyRegion.fTop, + mDirtyRegion.fRight, mDirtyRegion.fBottom, &paint); + } } - prepareShapes(canvas->getViewportHeight()); - drawGraph(canvas); - drawCurrentFrame(canvas); - drawThreshold(canvas); + if (mType == kBars) { + prepareShapes(canvas->getViewportHeight()); + drawGraph(canvas); + drawCurrentFrame(canvas); + drawThreshold(canvas); + } } void DrawProfiler::createData() { @@ -217,6 +231,7 @@ DrawProfiler::ProfileType DrawProfiler::loadRequestedProfileType() { } bool DrawProfiler::loadSystemProperties() { + bool changed = false; ProfileType newType = loadRequestedProfileType(); if (newType != mType) { mType = newType; @@ -225,13 +240,18 @@ bool DrawProfiler::loadSystemProperties() { } else { createData(); } - return true; + changed = true; } - return false; + bool showDirty = property_get_bool(PROPERTY_DEBUG_SHOW_DIRTY_REGIONS, false); + if (showDirty != mShowDirtyRegions) { + mShowDirtyRegions = showDirty; + changed = true; + } + return changed; } void DrawProfiler::dumpData(int fd) { - RETURN_IF_DISABLED(); + RETURN_IF_PROFILING_DISABLED(); // This method logs the last N frames (where N is <= mDataSize) since the // last call to dumpData(). In other words if there's a dumpData(), draw frame, diff --git a/libs/hwui/DrawProfiler.h b/libs/hwui/DrawProfiler.h index 7c06e5d..de64088 100644 --- a/libs/hwui/DrawProfiler.h +++ b/libs/hwui/DrawProfiler.h @@ -88,6 +88,10 @@ private: * information. */ float** mRects; + + bool mShowDirtyRegions; + SkRect mDirtyRegion; + bool mFlashToggle; }; } /* namespace uirenderer */ diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index b5089aa..9aa29ca 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -20,11 +20,18 @@ #include "Caches.h" #include "DeferredDisplayList.h" -#include "RenderState.h" #include "Layer.h" #include "LayerRenderer.h" #include "OpenGLRenderer.h" #include "RenderNode.h" +#include "RenderState.h" +#include "utils/TraceUtils.h" + +#define ATRACE_LAYER_WORK(label) \ + ATRACE_FORMAT("%s HW Layer DisplayList %s %ux%u", \ + label, \ + (renderNode.get() != NULL) ? renderNode->getName() : "", \ + getWidth(), getHeight()) namespace android { namespace uirenderer { @@ -35,6 +42,9 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth , renderState(renderState) , texture(caches) , type(layerType) { + // TODO: This is a violation of Android's typical ref counting, but it + // preserves the old inc/dec ref locations. This should be changed... + incStrong(0); mesh = NULL; meshElementCount = 0; cacheable = true; @@ -53,20 +63,14 @@ Layer::Layer(Type layerType, RenderState& renderState, const uint32_t layerWidth forceFilter = false; deferredList = NULL; convexMask = NULL; - caches.resourceCache.incrementRefcount(this); rendererLightPosDirty = true; wasBuildLayered = false; - if (!isTextureLayer()) { - // track only non-texture layer lifecycles in renderstate, - // because texture layers are destroyed via finalizer - renderState.registerLayer(this); - } + renderState.registerLayer(this); } Layer::~Layer() { - if (!isTextureLayer()) { - renderState.unregisterLayer(this); - } + renderState.requireGLContext(); + renderState.unregisterLayer(this); SkSafeUnref(colorFilter); removeFbo(); deleteTexture(); @@ -226,6 +230,8 @@ void Layer::allocateTexture() { } void Layer::defer(const OpenGLRenderer& rootRenderer) { + ATRACE_LAYER_WORK("Optimize"); + updateLightPosFromRenderer(rootRenderer); const float width = layer.getWidth(); const float height = layer.getHeight(); @@ -263,6 +269,9 @@ void Layer::cancelDefer() { void Layer::flush() { // renderer is checked as layer may be destroyed/put in layer cache with flush scheduled if (deferredList && renderer) { + ATRACE_LAYER_WORK("Issue"); + renderer->startMark((renderNode.get() != NULL) ? renderNode->getName() : "Layer"); + renderer->setViewport(layer.getWidth(), layer.getHeight()); renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, !isBlend()); @@ -273,10 +282,14 @@ void Layer::flush() { dirtyRect.setEmpty(); renderNode = NULL; + + renderer->endMark(); } } void Layer::render(const OpenGLRenderer& rootRenderer) { + ATRACE_LAYER_WORK("Direct-Issue"); + updateLightPosFromRenderer(rootRenderer); renderer->setViewport(layer.getWidth(), layer.getHeight()); renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom, @@ -292,5 +305,9 @@ void Layer::render(const OpenGLRenderer& rootRenderer) { renderNode = NULL; } +void Layer::postDecStrong() { + renderState.postDecStrong(this); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index b264595..2d6a727 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -52,7 +52,7 @@ struct DeferStateStruct; /** * A layer has dimensions and is backed by an OpenGL texture or FBO. */ -class Layer { +class Layer : public VirtualLightRefBase { public: enum Type { kType_Texture, @@ -280,6 +280,12 @@ public: void render(const OpenGLRenderer& rootRenderer); /** + * Posts a decStrong call to the appropriate thread. + * Thread-safe. + */ + void postDecStrong(); + + /** * Bounds of the layer. */ Rect layer; diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index cb7a350..3216cd2 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -86,7 +86,7 @@ void LayerCache::deleteLayer(Layer* layer) { } mSize -= layer->getWidth() * layer->getHeight() * 4; layer->state = Layer::kState_DeletedFromCache; - Caches::getInstance().resourceCache.decrementRefcount(layer); + layer->decStrong(0); } } diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index 103c843..83f9c6a 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -27,6 +27,7 @@ #include "Matrix.h" #include "Properties.h" #include "Rect.h" +#include "utils/TraceUtils.h" namespace android { namespace uirenderer { @@ -185,7 +186,7 @@ void LayerRenderer::generateMesh() { /////////////////////////////////////////////////////////////////////////////// Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) { - ATRACE_CALL(); + ATRACE_FORMAT("Allocate %ux%u HW Layer", width, height); LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height); Caches& caches = Caches::getInstance(); @@ -212,7 +213,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width // Creating a new layer always increment its refcount by 1, this allows // us to destroy the layer object if one was created for us - Caches::getInstance().resourceCache.decrementRefcount(layer); + layer->decStrong(0); return NULL; } @@ -240,7 +241,7 @@ Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width if (glGetError() != GL_NO_ERROR) { ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height); renderState.bindFramebuffer(previousFbo); - caches.resourceCache.decrementRefcount(layer); + layer->decStrong(0); return NULL; } } @@ -310,13 +311,13 @@ void LayerRenderer::updateTextureLayer(Layer* layer, uint32_t width, uint32_t he void LayerRenderer::destroyLayer(Layer* layer) { if (layer) { - ATRACE_CALL(); + ATRACE_FORMAT("Destroy %ux%u HW Layer", layer->getWidth(), layer->getHeight()); LAYER_RENDERER_LOGD("Recycling layer, %dx%d fbo = %d", layer->getWidth(), layer->getHeight(), layer->getFbo()); if (!Caches::getInstance().layerCache.put(layer)) { LAYER_RENDERER_LOGD(" Destroyed!"); - Caches::getInstance().resourceCache.decrementRefcount(layer); + layer->decStrong(0); } else { LAYER_RENDERER_LOGD(" Cached!"); #if DEBUG_LAYER_RENDERER @@ -328,14 +329,6 @@ void LayerRenderer::destroyLayer(Layer* layer) { } } -void LayerRenderer::destroyLayerDeferred(Layer* layer) { - if (layer) { - LAYER_RENDERER_LOGD("Deferring layer destruction, fbo = %d", layer->getFbo()); - - Caches::getInstance().deleteLayerDeferred(layer); - } -} - void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) { #ifdef GL_EXT_discard_framebuffer if (!layer) return; diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index bf7828c..4d8620b 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -60,7 +60,6 @@ public: static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height, bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform); static void destroyLayer(Layer* layer); - ANDROID_API static void destroyLayerDeferred(Layer* layer); static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap); static void flushLayer(RenderState& renderState, Layer* layer); diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index fdfcbe7..355a31f 100755 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -42,6 +42,7 @@ #include "ShadowTessellator.h" #include "SkiaShader.h" #include "utils/GLUtils.h" +#include "utils/TraceUtils.h" #include "Vector.h" #include "VertexBuffer.h" @@ -136,7 +137,6 @@ OpenGLRenderer::OpenGLRenderer(RenderState& renderState) , mScissorOptimizationDisabled(false) , mSuppressTiling(false) , mFirstFrameAfterResize(true) - , mCountOverdraw(false) , mLightCenter((Vector3){FLT_MIN, FLT_MIN, FLT_MIN}) , mLightRadius(FLT_MIN) , mAmbientShadowAlpha(0) @@ -251,7 +251,7 @@ void OpenGLRenderer::discardFramebuffer(float left, float top, float right, floa } status_t OpenGLRenderer::clear(float left, float top, float right, float bottom, bool opaque) { - if (!opaque || mCountOverdraw) { + if (!opaque) { mCaches.enableScissor(); mCaches.setScissor(left, getViewportHeight() - bottom, right - left, bottom - top); glClear(GL_COLOR_BUFFER_BIT); @@ -311,6 +311,11 @@ void OpenGLRenderer::finish() { renderOverdraw(); endTiling(); + for (size_t i = 0; i < mTempPaths.size(); i++) { + delete mTempPaths[i]; + } + mTempPaths.clear(); + // When finish() is invoked on FBO 0 we've reached the end // of the current frame if (getTargetFbo() == 0) { @@ -332,10 +337,6 @@ void OpenGLRenderer::finish() { #endif } - if (mCountOverdraw) { - countOverdraw(); - } - mFrameStarted = false; } @@ -401,6 +402,8 @@ void OpenGLRenderer::eventMarkDEBUG(const char* fmt, ...) const { va_end(ap); eventMark(buf); +#else + (void)fmt; #endif } @@ -449,21 +452,6 @@ void OpenGLRenderer::renderOverdraw() { } } -void OpenGLRenderer::countOverdraw() { - size_t count = getWidth() * getHeight(); - uint32_t* buffer = new uint32_t[count]; - glReadPixels(0, 0, getWidth(), getHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &buffer[0]); - - size_t total = 0; - for (size_t i = 0; i < count; i++) { - total += buffer[i] & 0xff; - } - - mOverdraw = total / float(count); - - delete[] buffer; -} - /////////////////////////////////////////////////////////////////////////////// // Layers /////////////////////////////////////////////////////////////////////////////// @@ -471,7 +459,6 @@ void OpenGLRenderer::countOverdraw() { bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) { if (layer->deferredUpdateScheduled && layer->renderer && layer->renderNode.get() && layer->renderNode->isRenderable()) { - ATRACE_CALL(); if (inFrame) { endTiling(); @@ -512,11 +499,8 @@ void OpenGLRenderer::updateLayers() { // Note: it is very important to update the layers in order for (int i = 0; i < count; i++) { - Layer* layer = mLayerUpdates.itemAt(i); + Layer* layer = mLayerUpdates.itemAt(i).get(); updateLayer(layer, false); - if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { - mCaches.resourceCache.decrementRefcount(layer); - } } if (CC_UNLIKELY(mCaches.drawDeferDisabled)) { @@ -531,21 +515,10 @@ void OpenGLRenderer::flushLayers() { int count = mLayerUpdates.size(); if (count > 0) { startMark("Apply Layer Updates"); - char layerName[12]; // Note: it is very important to update the layers in order for (int i = 0; i < count; i++) { - sprintf(layerName, "Layer #%d", i); - startMark(layerName); - - ATRACE_BEGIN("flushLayer"); - Layer* layer = mLayerUpdates.itemAt(i); - layer->flush(); - ATRACE_END(); - - mCaches.resourceCache.decrementRefcount(layer); - - endMark(); + mLayerUpdates.itemAt(i)->flush(); } mLayerUpdates.clear(); @@ -567,7 +540,6 @@ void OpenGLRenderer::pushLayerUpdate(Layer* layer) { } } mLayerUpdates.push_back(layer); - mCaches.resourceCache.incrementRefcount(layer); } } @@ -576,27 +548,14 @@ void OpenGLRenderer::cancelLayerUpdate(Layer* layer) { for (int i = mLayerUpdates.size() - 1; i >= 0; i--) { if (mLayerUpdates.itemAt(i) == layer) { mLayerUpdates.removeAt(i); - mCaches.resourceCache.decrementRefcount(layer); break; } } } } -void OpenGLRenderer::clearLayerUpdates() { - size_t count = mLayerUpdates.size(); - if (count > 0) { - mCaches.resourceCache.lock(); - for (size_t i = 0; i < count; i++) { - mCaches.resourceCache.decrementRefcountLocked(mLayerUpdates.itemAt(i)); - } - mCaches.resourceCache.unlock(); - mLayerUpdates.clear(); - } -} - void OpenGLRenderer::flushLayerUpdates() { - ATRACE_CALL(); + ATRACE_NAME("Update HW Layers"); syncState(); updateLayers(); flushLayers(); @@ -629,6 +588,7 @@ void OpenGLRenderer::onSnapshotRestored(const Snapshot& removed, const Snapshot& if (restoreLayer) { endMark(); // Savelayer + ATRACE_END(); // SaveLayer startMark("ComposeLayer"); composeLayer(removed, restored); endMark(); @@ -814,6 +774,9 @@ bool OpenGLRenderer::createLayer(float left, float top, float right, float botto mSnapshot->flags |= Snapshot::kFlagIsLayer; mSnapshot->layer = layer; + ATRACE_FORMAT_BEGIN("%ssaveLayer %ux%u", + fboLayer ? "" : "unclipped ", + layer->getWidth(), layer->getHeight()); startMark("SaveLayer"); if (fboLayer) { return createFboLayer(layer, bounds, clip); @@ -958,7 +921,7 @@ void OpenGLRenderer::composeLayer(const Snapshot& removed, const Snapshot& resto if (kDebugLayers) { ALOGD("Deleting layer"); } - Caches::getInstance().resourceCache.decrementRefcount(layer); + layer->decStrong(0); } } @@ -1623,8 +1586,6 @@ void OpenGLRenderer::setupDraw(bool clearLayer) { mDescription.hasDebugHighlight = !mCaches.debugOverdraw && mCaches.debugStencilClip == Caches::kStencilShowHighlight && mCaches.stencil.isTestEnabled(); - - mDescription.emulateStencil = mCountOverdraw; } void OpenGLRenderer::setupDrawWithTexture(bool isAlpha8) { @@ -1710,13 +1671,6 @@ void OpenGLRenderer::accountForClear(SkXfermode::Mode mode) { } } -static bool isBlendedColorFilter(const SkColorFilter* filter) { - if (filter == NULL) { - return false; - } - return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; -} - void OpenGLRenderer::setupDrawBlending(const Layer* layer, bool swapSrcDst) { SkXfermode::Mode mode = layer->getMode(); // When the blending mode is kClear_Mode, we need to use a modulate color @@ -1963,7 +1917,9 @@ status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int return status | replayStruct.mDrawGlStatus; } - bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs! + // Don't avoid overdraw when visualizing, since that makes it harder to + // debug where it's coming from, and when the problem occurs. + bool avoidOverdraw = !mCaches.debugOverdraw; DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw); DeferStateStruct deferStruct(deferredList, *this, replayFlags); renderNode->defer(deferStruct, 0); @@ -2104,7 +2060,7 @@ status_t OpenGLRenderer::drawBitmapMesh(const SkBitmap* bitmap, int meshWidth, i } mCaches.activeTexture(0); - Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); + Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap); const UvMapper& mapper(getMapper(texture)); for (int32_t y = 0; y < meshHeight; y++) { @@ -2287,7 +2243,7 @@ status_t OpenGLRenderer::drawPatch(const SkBitmap* bitmap, const Res_png_9patch* return DrawGlInfo::kStatusDone; } - AssetAtlas::Entry* entry = mCaches.assetAtlas.getEntry(bitmap); + AssetAtlas::Entry* entry = mRenderState.assetAtlas().getEntry(bitmap); const Patch* mesh = mCaches.patchCache.get(entry, bitmap->width(), bitmap->height(), right - left, bottom - top, patch); @@ -3083,7 +3039,7 @@ const SkPaint* OpenGLRenderer::filterPaint(const SkPaint* paint) { /////////////////////////////////////////////////////////////////////////////// Texture* OpenGLRenderer::getTexture(const SkBitmap* bitmap) { - Texture* texture = mCaches.assetAtlas.getEntryTexture(bitmap); + Texture* texture = mRenderState.assetAtlas().getEntryTexture(bitmap); if (!texture) { return mCaches.textureCache.get(bitmap); } @@ -3426,19 +3382,6 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, } mSkipOutlineClip = true; - if (mCountOverdraw) { - if (!mCaches.blend) glEnable(GL_BLEND); - if (mCaches.lastSrcMode != GL_ONE || mCaches.lastDstMode != GL_ONE) { - glBlendFunc(GL_ONE, GL_ONE); - } - - mCaches.blend = true; - mCaches.lastSrcMode = GL_ONE; - mCaches.lastDstMode = GL_ONE; - - return; - } - blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 47ef1a9..5eee2e2 100755 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -136,19 +136,10 @@ public: virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque); virtual void finish(); - void setCountOverdrawEnabled(bool enabled) { - mCountOverdraw = enabled; - } - - float getOverdraw() { - return mCountOverdraw ? mOverdraw : 0.0f; - } - virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty); void pushLayerUpdate(Layer* layer); void cancelLayerUpdate(Layer* layer); - void clearLayerUpdates(); void flushLayerUpdates(); void markLayersAsBuildLayers(); @@ -351,6 +342,12 @@ public: uint8_t getAmbientShadowAlpha() const { return mAmbientShadowAlpha; } uint8_t getSpotShadowAlpha() const { return mSpotShadowAlpha; } + SkPath* allocPathForFrame() { + SkPath* path = new SkPath(); + mTempPaths.push_back(path); + return path; + } + protected: /** * Perform the setup specific to a frame. This method does not @@ -990,7 +987,7 @@ private: // List of rectangles to clear after saveLayer() is invoked Vector<Rect*> mLayers; // List of layers to update at the beginning of a frame - Vector<Layer*> mLayerUpdates; + Vector< sp<Layer> > mLayerUpdates; // The following fields are used to setup drawing // Used to describe the shaders to generate @@ -1015,11 +1012,6 @@ private: bool mSuppressTiling; bool mFirstFrameAfterResize; - // If true, this renderer will setup drawing to emulate - // an increment stencil buffer in the color buffer - bool mCountOverdraw; - float mOverdraw; - bool mSkipOutlineClip; // Lighting + shadows @@ -1028,6 +1020,9 @@ private: uint8_t mAmbientShadowAlpha; uint8_t mSpotShadowAlpha; + // Paths kept alive for the duration of the frame + std::vector<SkPath*> mTempPaths; + friend class Layer; friend class TextSetupFunctor; friend class DrawBitmapOp; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index a3d7019..80f9c2f 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -255,7 +255,7 @@ void PathCache::trim() { PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, const SkPaint* paint) { - ATRACE_CALL(); + ATRACE_NAME("Generate Path Texture"); float left, top, offset; uint32_t width, height; diff --git a/libs/hwui/PathTessellator.cpp b/libs/hwui/PathTessellator.cpp index b3f779f..9f7dd50 100644 --- a/libs/hwui/PathTessellator.cpp +++ b/libs/hwui/PathTessellator.cpp @@ -811,7 +811,7 @@ void PathTessellator::tessellatePoints(const float* points, int count, const SkP // determine point shape SkPath path; float radius = paintInfo.halfStrokeWidth; - if (radius == 0.0f) radius = 0.25f; + if (radius == 0.0f) radius = 0.5f; if (paintInfo.cap == SkPaint::kRound_Cap) { path.addCircle(0, 0, radius); diff --git a/libs/hwui/Program.cpp b/libs/hwui/Program.cpp index cc72ae0..e6fd2dc 100644 --- a/libs/hwui/Program.cpp +++ b/libs/hwui/Program.cpp @@ -132,7 +132,7 @@ int Program::getUniform(const char* name) { } GLuint Program::buildShader(const char* source, GLenum type) { - ATRACE_CALL(); + ATRACE_NAME("Build GL Shader"); GLuint shader = glCreateShader(type); glShaderSource(shader, 1, &source, 0); @@ -141,11 +141,12 @@ GLuint Program::buildShader(const char* source, GLenum type) { GLint status; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); if (status != GL_TRUE) { + ALOGE("Error while compiling this shader:\n===\n%s\n===", source); // Some drivers return wrong values for GL_INFO_LOG_LENGTH // use a fixed size instead GLchar log[512]; glGetShaderInfoLog(shader, sizeof(log), 0, &log[0]); - LOG_ALWAYS_FATAL("Error while compiling shader: %s", log); + LOG_ALWAYS_FATAL("Shader info log: %s", log); return 0; } diff --git a/libs/hwui/Program.h b/libs/hwui/Program.h index 56773f4..fc7d134 100644 --- a/libs/hwui/Program.h +++ b/libs/hwui/Program.h @@ -84,8 +84,7 @@ namespace uirenderer { #define PROGRAM_HAS_COLORS 42 #define PROGRAM_HAS_DEBUG_HIGHLIGHT 43 -#define PROGRAM_EMULATE_STENCIL 44 -#define PROGRAM_HAS_ROUND_RECT_CLIP 45 +#define PROGRAM_HAS_ROUND_RECT_CLIP 44 /////////////////////////////////////////////////////////////////////////////// // Types @@ -161,7 +160,6 @@ struct ProgramDescription { float gamma; bool hasDebugHighlight; - bool emulateStencil; bool hasRoundRectClip; /** @@ -204,7 +202,6 @@ struct ProgramDescription { gamma = 2.2f; hasDebugHighlight = false; - emulateStencil = false; hasRoundRectClip = false; } @@ -272,7 +269,6 @@ struct ProgramDescription { if (isSimpleGradient) key |= programid(0x1) << PROGRAM_IS_SIMPLE_GRADIENT; if (hasColors) key |= programid(0x1) << PROGRAM_HAS_COLORS; if (hasDebugHighlight) key |= programid(0x1) << PROGRAM_HAS_DEBUG_HIGHLIGHT; - if (emulateStencil) key |= programid(0x1) << PROGRAM_EMULATE_STENCIL; if (hasRoundRectClip) key |= programid(0x1) << PROGRAM_HAS_ROUND_RECT_CLIP; return key; } @@ -285,6 +281,8 @@ struct ProgramDescription { programid k = key(); PROGRAM_LOGD("%s (key = 0x%.8x%.8x)", message, uint32_t(k >> 32), uint32_t(k & 0xffffffff)); +#else + (void)message; #endif } diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index 06353c0..62835e0 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -347,12 +347,6 @@ const char* gFS_Main_FragColor_HasRoundRectClip = const char* gFS_Main_DebugHighlight = " gl_FragColor.rgb = vec3(0.0, gl_FragColor.a, 0.0);\n"; -const char* gFS_Main_EmulateStencil = - " gl_FragColor.rgba = vec4(1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0, 1.0);\n" - " return;\n" - " /*\n"; -const char* gFS_Footer_EmulateStencil = - " */\n"; const char* gFS_Footer = "}\n\n"; @@ -617,7 +611,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti && !description.hasColors && description.colorOp == ProgramDescription::kColorNone && !description.hasDebugHighlight - && !description.emulateStencil && !description.hasRoundRectClip) { bool fast = false; @@ -698,9 +691,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti // Begin the shader shader.append(gFS_Main); { - if (description.emulateStencil) { - shader.append(gFS_Main_EmulateStencil); - } // Stores the result in fragColor directly if (description.hasTexture || description.hasExternalTexture) { if (description.hasAlpha8Texture) { @@ -779,9 +769,6 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti shader.append(gFS_Main_DebugHighlight); } } - if (description.emulateStencil) { - shader.append(gFS_Footer_EmulateStencil); - } // End the shader shader.append(gFS_Footer); diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 8d529c6..7b9459a 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -133,6 +133,15 @@ enum DebugLevel { #define PROPERTY_DEBUG_STENCIL_CLIP "debug.hwui.show_non_rect_clip" /** + * Turn on to draw dirty regions every other frame. + * + * Possible values: + * "true", to enable dirty regions debugging + * "false", to disable dirty regions debugging + */ +#define PROPERTY_DEBUG_SHOW_DIRTY_REGIONS "debug.hwui.show_dirty_regions" + +/** * Disables draw operation deferral if set to "true", forcing draw * commands to be issued to OpenGL in order, and processed in sequence * with state-manipulation canvas commands. diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 22808e2..c993556 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -25,7 +25,6 @@ #include <SkCanvas.h> #include <algorithm> -#include <utils/Trace.h> #include "DamageAccumulator.h" #include "Debug.h" @@ -34,6 +33,7 @@ #include "LayerRenderer.h" #include "OpenGLRenderer.h" #include "utils/MathUtils.h" +#include "utils/TraceUtils.h" #include "renderthread/CanvasContext.h" namespace android { @@ -50,10 +50,13 @@ void RenderNode::outputLogBuffer(int fd) { fprintf(file, "\nRecent DisplayList operations\n"); logBuffer.outputCommands(file); - String8 cachesLog; - Caches::getInstance().dumpMemoryUsage(cachesLog); - fprintf(file, "\nCaches:\n%s", cachesLog.string()); - fprintf(file, "\n"); + if (Caches::hasInstance()) { + String8 cachesLog; + Caches::getInstance().dumpMemoryUsage(cachesLog); + fprintf(file, "\nCaches:\n%s\n", cachesLog.string()); + } else { + fprintf(file, "\nNo caches instance.\n"); + } fflush(file); } @@ -84,16 +87,17 @@ RenderNode::RenderNode() RenderNode::~RenderNode() { deleteDisplayListData(); delete mStagingDisplayListData; - LayerRenderer::destroyLayerDeferred(mLayer); + if (mLayer) { + ALOGW("Memory Warning: Layer %p missed its detachment, held on to for far too long!", mLayer); + mLayer->postDecStrong(); + mLayer = 0; + } } void RenderNode::setStagingDisplayList(DisplayListData* data) { mNeedsDisplayListDataSync = true; delete mStagingDisplayListData; mStagingDisplayListData = data; - if (mStagingDisplayListData) { - Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size()); - } } /** @@ -101,8 +105,11 @@ void RenderNode::setStagingDisplayList(DisplayListData* data) { * display list. This function should remain in sync with the replay() function. */ void RenderNode::output(uint32_t level) { - ALOGD("%*sStart display list (%p, %s, render=%d)", (level - 1) * 2, "", this, - getName(), isRenderable()); + ALOGD("%*sStart display list (%p, %s%s%s%s)", (level - 1) * 2, "", this, + getName(), + (properties().hasShadow() ? ", casting shadow" : ""), + (isRenderable() ? "" : ", empty"), + (mLayer != NULL ? ", on HW Layer" : "")); ALOGD("%*s%s %d", level * 2, "", "Save", SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag); @@ -198,6 +205,7 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) { info.damageAccumulator->peekAtDirty(&dirty); if (!mLayer) { + Caches::getInstance().dumpMemoryUsage(); if (info.errorHandler) { std::string msg = "Unable to create layer for "; msg += getName(); @@ -293,7 +301,14 @@ void RenderNode::pushStagingDisplayListChanges(TreeInfo& info) { mStagingDisplayListData->children()[i]->mRenderNode->incParentRefCount(); } } + // Damage with the old display list first then the new one to catch any + // changes in isRenderable or, in the future, bounds + damageSelf(info); deleteDisplayListData(); + // TODO: Remove this caches stuff + if (mStagingDisplayListData && mStagingDisplayListData->functors.size()) { + Caches::getInstance().registerFunctors(mStagingDisplayListData->functors.size()); + } mDisplayListData = mStagingDisplayListData; mStagingDisplayListData = NULL; if (mDisplayListData) { @@ -310,6 +325,9 @@ void RenderNode::deleteDisplayListData() { for (size_t i = 0; i < mDisplayListData->children().size(); i++) { mDisplayListData->children()[i]->mRenderNode->decParentRefCount(); } + if (mDisplayListData->functors.size()) { + Caches::getInstance().unregisterFunctors(mDisplayListData->functors.size()); + } } delete mDisplayListData; mDisplayListData = NULL; @@ -411,6 +429,10 @@ void RenderNode::setViewProperties(OpenGLRenderer& renderer, T& handler) { clipFlags = 0; // all clipping done by saveLayer } + ATRACE_FORMAT("%s alpha caused %ssaveLayer %dx%d", + getName(), clipFlags ? "" : "unclipped ", + (int)layerBounds.getWidth(), (int)layerBounds.getHeight()); + SaveLayerOp* op = new (handler.allocator()) SaveLayerOp( layerBounds.left, layerBounds.top, layerBounds.right, layerBounds.bottom, properties().getAlpha() * 255, saveFlags); @@ -658,13 +680,32 @@ void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& float casterAlpha = properties().getAlpha() * properties().getOutline().getAlpha(); + + // holds temporary SkPath to store the result of intersections + SkPath* frameAllocatedPath = NULL; const SkPath* outlinePath = casterOutlinePath; + + // intersect the outline with the reveal clip, if present if (revealClipPath) { - // if we can't simply use the caster's path directly, create a temporary one - SkPath* frameAllocatedPath = handler.allocPathForFrame(); + frameAllocatedPath = handler.allocPathForFrame(); + + Op(*outlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath); + outlinePath = frameAllocatedPath; + } + + // intersect the outline with the clipBounds, if present + if (properties().getClippingFlags() & CLIP_TO_CLIP_BOUNDS) { + if (!frameAllocatedPath) { + frameAllocatedPath = handler.allocPathForFrame(); + } + + Rect clipBounds; + properties().getClippingRectForFlags(CLIP_TO_CLIP_BOUNDS, &clipBounds); + SkPath clipBoundsPath; + clipBoundsPath.addRect(clipBounds.left, clipBounds.top, + clipBounds.right, clipBounds.bottom); - // intersect the outline with the convex reveal clip - Op(*casterOutlinePath, *revealClipPath, kIntersect_PathOp, frameAllocatedPath); + Op(*outlinePath, clipBoundsPath, kIntersect_PathOp, frameAllocatedPath); outlinePath = frameAllocatedPath; } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index f329283..2ce7cb7 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -101,7 +101,7 @@ public: kReplayFlag_ClipChildren = 0x1 }; - ANDROID_API static void outputLogBuffer(int fd); + static void outputLogBuffer(int fd); void debugDumpLayers(const char* prefix); ANDROID_API void setStagingDisplayList(DisplayListData* newData); diff --git a/libs/hwui/RenderProperties.h b/libs/hwui/RenderProperties.h index b936d4b..31c4f22 100644 --- a/libs/hwui/RenderProperties.h +++ b/libs/hwui/RenderProperties.h @@ -570,7 +570,7 @@ public: } bool hasShadow() const { - return getZ() >= 0.0f + return getZ() > 0.0f && getOutline().getPath() != NULL && getOutline().getAlpha() != 0.0f; } diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp index 86bd7dd..d1f5f4e 100644 --- a/libs/hwui/RenderState.cpp +++ b/libs/hwui/RenderState.cpp @@ -16,15 +16,18 @@ #include "RenderState.h" #include "renderthread/CanvasContext.h" +#include "renderthread/EglManager.h" namespace android { namespace uirenderer { -RenderState::RenderState() - : mCaches(NULL) +RenderState::RenderState(renderthread::RenderThread& thread) + : mRenderThread(thread) + , mCaches(NULL) , mViewportWidth(0) , mViewportHeight(0) , mFramebuffer(0) { + mThreadId = pthread_self(); } RenderState::~RenderState() { @@ -35,11 +38,11 @@ void RenderState::onGLContextCreated() { mCaches = &Caches::getInstance(); mCaches->init(); mCaches->setRenderState(this); + mCaches->textureCache.setAssetAtlas(&mAssetAtlas); } void RenderState::onGLContextDestroyed() { /* - AutoMutex _lock(mLayerLock); size_t size = mActiveLayers.size(); if (CC_UNLIKELY(size != 0)) { ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d", @@ -70,6 +73,7 @@ void RenderState::onGLContextDestroyed() { LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size); } */ + mAssetAtlas.terminate(); } void RenderState::setViewport(GLsizei width, GLsizei height) { @@ -146,5 +150,34 @@ void RenderState::debugOverdraw(bool enable, bool clear) { } } +void RenderState::requireGLContext() { + assertOnGLThread(); + mRenderThread.eglManager().requireGlContext(); +} + +void RenderState::assertOnGLThread() { + pthread_t curr = pthread_self(); + LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!"); +} + + +class DecStrongTask : public renderthread::RenderTask { +public: + DecStrongTask(VirtualLightRefBase* object) : mObject(object) {} + + virtual void run() { + mObject->decStrong(0); + mObject = 0; + delete this; + } + +private: + VirtualLightRefBase* mObject; +}; + +void RenderState::postDecStrong(VirtualLightRefBase* object) { + mRenderThread.queue(new DecStrongTask(object)); +} + } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h index cbe7cfc..1ecfb1c 100644 --- a/libs/hwui/RenderState.h +++ b/libs/hwui/RenderState.h @@ -23,6 +23,7 @@ #include <private/hwui/DrawGlInfo.h> +#include "AssetAtlas.h" #include "Caches.h" #include "utils/Macros.h" @@ -53,16 +54,10 @@ public: void debugOverdraw(bool enable, bool clear); void registerLayer(const Layer* layer) { - /* - AutoMutex _lock(mLayerLock); mActiveLayers.insert(layer); - */ } void unregisterLayer(const Layer* layer) { - /* - AutoMutex _lock(mLayerLock); mActiveLayers.erase(layer); - */ } void registerCanvasContext(renderthread::CanvasContext* context) { @@ -73,24 +68,36 @@ public: mRegisteredContexts.erase(context); } + void requireGLContext(); + + // TODO: This system is a little clunky feeling, this could use some + // more thinking... + void postDecStrong(VirtualLightRefBase* object); + + AssetAtlas& assetAtlas() { return mAssetAtlas; } + private: friend class renderthread::RenderThread; friend class Caches; void interruptForFunctorInvoke(); void resumeFromFunctorInvoke(); + void assertOnGLThread(); - RenderState(); + RenderState(renderthread::RenderThread& thread); ~RenderState(); + renderthread::RenderThread& mRenderThread; Caches* mCaches; + AssetAtlas mAssetAtlas; std::set<const Layer*> mActiveLayers; std::set<renderthread::CanvasContext*> mRegisteredContexts; GLsizei mViewportWidth; GLsizei mViewportHeight; GLuint mFramebuffer; - Mutex mLayerLock; + + pthread_t mThreadId; }; } /* namespace uirenderer */ diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h index 9cedd5a..3159d1e 100644 --- a/libs/hwui/Renderer.h +++ b/libs/hwui/Renderer.h @@ -17,12 +17,13 @@ #ifndef ANDROID_HWUI_RENDERER_H #define ANDROID_HWUI_RENDERER_H +#include <SkColorFilter.h> +#include <SkPaint.h> #include <SkRegion.h> #include <utils/String8.h> #include "AssetAtlas.h" -#include "SkPaint.h" namespace android { @@ -81,6 +82,14 @@ public: && !paint.getColorFilter() && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; } + + static bool isBlendedColorFilter(const SkColorFilter* filter) { + if (filter == NULL) { + return false; + } + return (filter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag) == 0; + } + // ---------------------------------------------------------------------------- // Frame state operations // ---------------------------------------------------------------------------- @@ -211,7 +220,6 @@ public: // ---------------------------------------------------------------------------- // Canvas draw operations - special // ---------------------------------------------------------------------------- - virtual status_t drawLayer(Layer* layer, float x, float y) = 0; virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) = 0; diff --git a/libs/hwui/ResourceCache.cpp b/libs/hwui/ResourceCache.cpp index 8b553d1..31bd637 100644 --- a/libs/hwui/ResourceCache.cpp +++ b/libs/hwui/ResourceCache.cpp @@ -21,6 +21,12 @@ #include "Caches.h" namespace android { + +#ifdef USE_OPENGL_RENDERER +using namespace uirenderer; +ANDROID_SINGLETON_STATIC_INSTANCE(ResourceCache); +#endif + namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// @@ -62,8 +68,6 @@ void ResourceCache::incrementRefcount(void* resource, ResourceType resourceType) } void ResourceCache::incrementRefcount(const SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->globalRef(); - SkSafeRef(bitmapResource->getColorTable()); incrementRefcount((void*) bitmapResource, kBitmap); } @@ -75,10 +79,6 @@ void ResourceCache::incrementRefcount(const Res_png_9patch* patchResource) { incrementRefcount((void*) patchResource, kNinePatch); } -void ResourceCache::incrementRefcount(Layer* layerResource) { - incrementRefcount((void*) layerResource, kLayer); -} - void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourceType) { ssize_t index = mCache->indexOfKey(resource); ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; @@ -90,8 +90,6 @@ void ResourceCache::incrementRefcountLocked(void* resource, ResourceType resourc } void ResourceCache::incrementRefcountLocked(const SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->globalRef(); - SkSafeRef(bitmapResource->getColorTable()); incrementRefcountLocked((void*) bitmapResource, kBitmap); } @@ -103,18 +101,12 @@ void ResourceCache::incrementRefcountLocked(const Res_png_9patch* patchResource) incrementRefcountLocked((void*) patchResource, kNinePatch); } -void ResourceCache::incrementRefcountLocked(Layer* layerResource) { - incrementRefcountLocked((void*) layerResource, kLayer); -} - void ResourceCache::decrementRefcount(void* resource) { Mutex::Autolock _l(mLock); decrementRefcountLocked(resource); } void ResourceCache::decrementRefcount(const SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->globalUnref(); - SkSafeUnref(bitmapResource->getColorTable()); decrementRefcount((void*) bitmapResource); } @@ -126,10 +118,6 @@ void ResourceCache::decrementRefcount(const Res_png_9patch* patchResource) { decrementRefcount((void*) patchResource); } -void ResourceCache::decrementRefcount(Layer* layerResource) { - decrementRefcount((void*) layerResource); -} - void ResourceCache::decrementRefcountLocked(void* resource) { ssize_t index = mCache->indexOfKey(resource); ResourceReference* ref = index >= 0 ? mCache->valueAt(index) : NULL; @@ -144,8 +132,6 @@ void ResourceCache::decrementRefcountLocked(void* resource) { } void ResourceCache::decrementRefcountLocked(const SkBitmap* bitmapResource) { - bitmapResource->pixelRef()->globalUnref(); - SkSafeUnref(bitmapResource->getColorTable()); decrementRefcountLocked((void*) bitmapResource); } @@ -157,10 +143,6 @@ void ResourceCache::decrementRefcountLocked(const Res_png_9patch* patchResource) decrementRefcountLocked((void*) patchResource); } -void ResourceCache::decrementRefcountLocked(Layer* layerResource) { - decrementRefcountLocked((void*) layerResource); -} - void ResourceCache::destructor(SkPath* resource) { Mutex::Autolock _l(mLock); destructorLocked(resource); @@ -195,10 +177,9 @@ void ResourceCache::destructorLocked(const SkBitmap* resource) { if (ref == NULL) { // If we're not tracking this resource, just delete it if (Caches::hasInstance()) { - Caches::getInstance().textureCache.removeDeferred(resource); - } else { - delete resource; + Caches::getInstance().textureCache.releaseTexture(resource); } + delete resource; return; } ref->destroyed = true; @@ -248,6 +229,9 @@ bool ResourceCache::recycle(SkBitmap* resource) { bool ResourceCache::recycleLocked(SkBitmap* resource) { ssize_t index = mCache->indexOfKey(resource); if (index < 0) { + if (Caches::hasInstance()) { + Caches::getInstance().textureCache.releaseTexture(resource); + } // not tracking this resource; just recycle the pixel data resource->setPixels(NULL, NULL); return true; @@ -272,17 +256,20 @@ bool ResourceCache::recycleLocked(SkBitmap* resource) { */ void ResourceCache::deleteResourceReferenceLocked(const void* resource, ResourceReference* ref) { if (ref->recycled && ref->resourceType == kBitmap) { - ((SkBitmap*) resource)->setPixels(NULL, NULL); + SkBitmap* bitmap = (SkBitmap*) resource; + if (Caches::hasInstance()) { + Caches::getInstance().textureCache.releaseTexture(bitmap); + } + bitmap->setPixels(NULL, NULL); } - if (ref->destroyed || ref->resourceType == kLayer) { + if (ref->destroyed) { switch (ref->resourceType) { case kBitmap: { SkBitmap* bitmap = (SkBitmap*) resource; if (Caches::hasInstance()) { - Caches::getInstance().textureCache.removeDeferred(bitmap); - } else { - delete bitmap; + Caches::getInstance().textureCache.releaseTexture(bitmap); } + delete bitmap; } break; case kPath: { @@ -305,11 +292,6 @@ void ResourceCache::deleteResourceReferenceLocked(const void* resource, Resource } } break; - case kLayer: { - Layer* layer = (Layer*) resource; - Caches::getInstance().deleteLayerDeferred(layer); - } - break; } } mCache->removeItem(resource); diff --git a/libs/hwui/ResourceCache.h b/libs/hwui/ResourceCache.h index 3864d4b..f12f2a4 100644 --- a/libs/hwui/ResourceCache.h +++ b/libs/hwui/ResourceCache.h @@ -22,6 +22,7 @@ #include <SkBitmap.h> #include <utils/KeyedVector.h> +#include <utils/Singleton.h> #include <androidfw/ResourceTypes.h> @@ -36,14 +37,12 @@ namespace uirenderer { enum ResourceType { kBitmap, kNinePatch, - kPath, - kLayer + kPath }; class ResourceReference { public: - ResourceReference() { refCount = 0; recycled = false; destroyed = false;} ResourceReference(ResourceType type) { refCount = 0; recycled = false; destroyed = false; resourceType = type; } @@ -54,11 +53,14 @@ public: ResourceType resourceType; }; -class ANDROID_API ResourceCache { -public: +class ANDROID_API ResourceCache: public Singleton<ResourceCache> { ResourceCache(); ~ResourceCache(); + friend class Singleton<ResourceCache>; + +public: + /** * When using these two methods, make sure to only invoke the *Locked() * variants of increment/decrementRefcount(), recyle() and destructor() @@ -69,22 +71,18 @@ public: void incrementRefcount(const SkPath* resource); void incrementRefcount(const SkBitmap* resource); void incrementRefcount(const Res_png_9patch* resource); - void incrementRefcount(Layer* resource); void incrementRefcountLocked(const SkPath* resource); void incrementRefcountLocked(const SkBitmap* resource); void incrementRefcountLocked(const Res_png_9patch* resource); - void incrementRefcountLocked(Layer* resource); void decrementRefcount(const SkBitmap* resource); void decrementRefcount(const SkPath* resource); void decrementRefcount(const Res_png_9patch* resource); - void decrementRefcount(Layer* resource); void decrementRefcountLocked(const SkBitmap* resource); void decrementRefcountLocked(const SkPath* resource); void decrementRefcountLocked(const Res_png_9patch* resource); - void decrementRefcountLocked(Layer* resource); void destructor(SkPath* resource); void destructor(const SkBitmap* resource); diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 034c3f6..631305f 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -62,7 +62,8 @@ public: // This shader is unsupported. Skip it. } static void setupProgram(Caches* caches, const mat4& modelViewMatrix, - GLuint* textureUnit, const Extensions& extensions, const SkShader& shader) { + GLuint* textureUnit, const Extensions& extensions, + const SkShader& shader) { // This shader is unsupported. Skip it. } diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index dbedf94..3046fd5 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -60,7 +60,7 @@ namespace android { namespace uirenderer { -static const double EPSILON = 1e-7; +static const float EPSILON = 1e-7; /** * For each polygon's vertex, the light center will project it to the receiver @@ -118,17 +118,17 @@ static float rayIntersectPoints(const Vector2& rayOrigin, float dx, float dy, // intersection point should stay on both the ray and the edge of (p1, p2). // solve([p1x+t*(p2x-p1x)=dx*t2+px,p1y+t*(p2y-p1y)=dy*t2+py],[t,t2]); - double divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x); + float divisor = (dx * (p1.y - p2.y) + dy * p2.x - dy * p1.x); if (divisor == 0) return -1.0f; // error, invalid divisor #if DEBUG_SHADOW - double interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor; + float interpVal = (dx * (p1.y - rayOrigin.y) + dy * rayOrigin.x - dy * p1.x) / divisor; if (interpVal < 0 || interpVal > 1) { ALOGW("rayIntersectPoints is hitting outside the segment %f", interpVal); } #endif - double distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) + + float distance = (p1.x * (rayOrigin.y - p2.y) + p2.x * (p1.y - rayOrigin.y) + rayOrigin.x * (p2.y - p1.y)) / divisor; return distance; // may be negative in error cases @@ -217,146 +217,12 @@ int SpotShadow::hull(Vector2* points, int pointsLength, Vector2* retPoly) { * * @return true if a right hand turn */ -bool SpotShadow::ccw(double ax, double ay, double bx, double by, - double cx, double cy) { +bool SpotShadow::ccw(float ax, float ay, float bx, float by, + float cx, float cy) { return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax) > EPSILON; } /** - * Calculates the intersection of poly1 with poly2 and put in poly2. - * Note that both poly1 and poly2 must be in CW order already! - * - * @param poly1 The 1st polygon, as a Vector2 array. - * @param poly1Length The number of vertices of 1st polygon. - * @param poly2 The 2nd and output polygon, as a Vector2 array. - * @param poly2Length The number of vertices of 2nd polygon. - * @return number of vertices in output polygon as poly2. - */ -int SpotShadow::intersection(const Vector2* poly1, int poly1Length, - Vector2* poly2, int poly2Length) { -#if DEBUG_SHADOW - if (!ShadowTessellator::isClockwise(poly1, poly1Length)) { - ALOGW("Poly1 is not clockwise! Intersection is wrong!"); - } - if (!ShadowTessellator::isClockwise(poly2, poly2Length)) { - ALOGW("Poly2 is not clockwise! Intersection is wrong!"); - } -#endif - Vector2 poly[poly1Length * poly2Length + 2]; - int count = 0; - int pcount = 0; - - // If one vertex from one polygon sits inside another polygon, add it and - // count them. - for (int i = 0; i < poly1Length; i++) { - if (testPointInsidePolygon(poly1[i], poly2, poly2Length)) { - poly[count] = poly1[i]; - count++; - pcount++; - - } - } - - int insidePoly2 = pcount; - for (int i = 0; i < poly2Length; i++) { - if (testPointInsidePolygon(poly2[i], poly1, poly1Length)) { - poly[count] = poly2[i]; - count++; - } - } - - int insidePoly1 = count - insidePoly2; - // If all vertices from poly1 are inside poly2, then just return poly1. - if (insidePoly2 == poly1Length) { - memcpy(poly2, poly1, poly1Length * sizeof(Vector2)); - return poly1Length; - } - - // If all vertices from poly2 are inside poly1, then just return poly2. - if (insidePoly1 == poly2Length) { - return poly2Length; - } - - // Since neither polygon fully contain the other one, we need to add all the - // intersection points. - Vector2 intersection = {0, 0}; - for (int i = 0; i < poly2Length; i++) { - for (int j = 0; j < poly1Length; j++) { - int poly2LineStart = i; - int poly2LineEnd = ((i + 1) % poly2Length); - int poly1LineStart = j; - int poly1LineEnd = ((j + 1) % poly1Length); - bool found = lineIntersection( - poly2[poly2LineStart].x, poly2[poly2LineStart].y, - poly2[poly2LineEnd].x, poly2[poly2LineEnd].y, - poly1[poly1LineStart].x, poly1[poly1LineStart].y, - poly1[poly1LineEnd].x, poly1[poly1LineEnd].y, - intersection); - if (found) { - poly[count].x = intersection.x; - poly[count].y = intersection.y; - count++; - } else { - Vector2 delta = poly2[i] - poly1[j]; - if (delta.lengthSquared() < EPSILON) { - poly[count] = poly2[i]; - count++; - } - } - } - } - - if (count == 0) { - return 0; - } - - // Sort the result polygon around the center. - Vector2 center = {0.0f, 0.0f}; - for (int i = 0; i < count; i++) { - center += poly[i]; - } - center /= count; - sort(poly, count, center); - -#if DEBUG_SHADOW - // Since poly2 is overwritten as the result, we need to save a copy to do - // our verification. - Vector2 oldPoly2[poly2Length]; - int oldPoly2Length = poly2Length; - memcpy(oldPoly2, poly2, sizeof(Vector2) * poly2Length); -#endif - - // Filter the result out from poly and put it into poly2. - poly2[0] = poly[0]; - int lastOutputIndex = 0; - for (int i = 1; i < count; i++) { - Vector2 delta = poly[i] - poly2[lastOutputIndex]; - if (delta.lengthSquared() >= EPSILON) { - poly2[++lastOutputIndex] = poly[i]; - } else { - // If the vertices are too close, pick the inner one, because the - // inner one is more likely to be an intersection point. - Vector2 delta1 = poly[i] - center; - Vector2 delta2 = poly2[lastOutputIndex] - center; - if (delta1.lengthSquared() < delta2.lengthSquared()) { - poly2[lastOutputIndex] = poly[i]; - } - } - } - int resultLength = lastOutputIndex + 1; - -#if DEBUG_SHADOW - testConvex(poly2, resultLength, "intersection"); - testConvex(poly1, poly1Length, "input poly1"); - testConvex(oldPoly2, oldPoly2Length, "input poly2"); - - testIntersection(poly1, poly1Length, oldPoly2, oldPoly2Length, poly2, resultLength); -#endif - - return resultLength; -} - -/** * Sort points about a center point * * @param poly The in and out polyogon as a Vector2 array. @@ -441,13 +307,13 @@ void SpotShadow::quicksortX(Vector2* points, int low, int high) { bool SpotShadow::testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len) { bool c = false; - double testx = testPoint.x; - double testy = testPoint.y; + float testx = testPoint.x; + float testy = testPoint.y; for (int i = 0, j = len - 1; i < len; j = i++) { - double startX = poly[j].x; - double startY = poly[j].y; - double endX = poly[i].x; - double endY = poly[i].y; + float startX = poly[j].x; + float startY = poly[j].y; + float endX = poly[i].x; + float endY = poly[i].y; if (((endY > testy) != (startY > testy)) && (testx < (startX - endX) * (testy - endY) @@ -490,46 +356,6 @@ void SpotShadow::reverse(Vector2* polygon, int len) { } /** - * Intersects two lines in parametric form. This function is called in a tight - * loop, and we need double precision to get things right. - * - * @param x1 the x coordinate point 1 of line 1 - * @param y1 the y coordinate point 1 of line 1 - * @param x2 the x coordinate point 2 of line 1 - * @param y2 the y coordinate point 2 of line 1 - * @param x3 the x coordinate point 1 of line 2 - * @param y3 the y coordinate point 1 of line 2 - * @param x4 the x coordinate point 2 of line 2 - * @param y4 the y coordinate point 2 of line 2 - * @param ret the x,y location of the intersection - * @return true if it found an intersection - */ -inline bool SpotShadow::lineIntersection(double x1, double y1, double x2, double y2, - double x3, double y3, double x4, double y4, Vector2& ret) { - double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); - if (d == 0.0) return false; - - double dx = (x1 * y2 - y1 * x2); - double dy = (x3 * y4 - y3 * x4); - double x = (dx * (x3 - x4) - (x1 - x2) * dy) / d; - double y = (dx * (y3 - y4) - (y1 - y2) * dy) / d; - - // The intersection should be in the middle of the point 1 and point 2, - // likewise point 3 and point 4. - if (((x - x1) * (x - x2) > EPSILON) - || ((x - x3) * (x - x4) > EPSILON) - || ((y - y1) * (y - y2) > EPSILON) - || ((y - y3) * (y - y4) > EPSILON)) { - // Not interesected - return false; - } - ret.x = x; - ret.y = y; - return true; - -} - -/** * Compute a horizontal circular polygon about point (x , y , height) of radius * (size) * @@ -542,7 +368,7 @@ void SpotShadow::computeLightPolygon(int points, const Vector3& lightCenter, float size, Vector3* ret) { // TODO: Caching all the sin / cos values and store them in a look up table. for (int i = 0; i < points; i++) { - double angle = 2 * i * M_PI / points; + float angle = 2 * i * M_PI / points; ret[i].x = cosf(angle) * size + lightCenter.x; ret[i].y = sinf(angle) * size + lightCenter.y; ret[i].z = lightCenter.z; @@ -792,83 +618,6 @@ void SpotShadow::createSpotShadow(bool isCasterOpaque, const Vector3& lightCente } /** - * Converts a polygon specified with CW vertices into an array of distance-from-centroid values. - * - * Returns false in error conditions - * - * @param poly Array of vertices. Note that these *must* be CW. - * @param polyLength The number of vertices in the polygon. - * @param polyCentroid The centroid of the polygon, from which rays will be cast - * @param rayDist The output array for the calculated distances, must be SHADOW_RAY_COUNT in size - */ -bool convertPolyToRayDist(const Vector2* poly, int polyLength, const Vector2& polyCentroid, - float* rayDist) { - const int rays = SHADOW_RAY_COUNT; - const float step = M_PI * 2 / rays; - - const Vector2* lastVertex = &(poly[polyLength - 1]); - float startAngle = angle(*lastVertex, polyCentroid); - - // Start with the ray that's closest to and less than startAngle - int rayIndex = floor((startAngle - EPSILON) / step); - rayIndex = (rayIndex + rays) % rays; // ensure positive - - for (int polyIndex = 0; polyIndex < polyLength; polyIndex++) { - /* - * For a given pair of vertices on the polygon, poly[i-1] and poly[i], the rays that - * intersect these will be those that are between the two angles from the centroid that the - * vertices define. - * - * Because the polygon vertices are stored clockwise, the closest ray with an angle - * *smaller* than that defined by angle(poly[i], centroid) will be the first ray that does - * not intersect with poly[i-1], poly[i]. - */ - float currentAngle = angle(poly[polyIndex], polyCentroid); - - // find first ray that will not intersect the line segment poly[i-1] & poly[i] - int firstRayIndexOnNextSegment = floor((currentAngle - EPSILON) / step); - firstRayIndexOnNextSegment = (firstRayIndexOnNextSegment + rays) % rays; // ensure positive - - // Iterate through all rays that intersect with poly[i-1], poly[i] line segment. - // This may be 0 rays. - while (rayIndex != firstRayIndexOnNextSegment) { - float distanceToIntersect = rayIntersectPoints(polyCentroid, - cos(rayIndex * step), - sin(rayIndex * step), - *lastVertex, poly[polyIndex]); - if (distanceToIntersect < 0) { -#if DEBUG_SHADOW - ALOGW("ERROR: convertPolyToRayDist failed"); -#endif - return false; // error case, abort - } - - rayDist[rayIndex] = distanceToIntersect; - - rayIndex = (rayIndex - 1 + rays) % rays; - } - lastVertex = &poly[polyIndex]; - } - - return true; -} - -int SpotShadow::calculateOccludedUmbra(const Vector2* umbra, int umbraLength, - const Vector3* poly, int polyLength, Vector2* occludedUmbra) { - // Occluded umbra area is computed as the intersection of the projected 2D - // poly and umbra. - for (int i = 0; i < polyLength; i++) { - occludedUmbra[i].x = poly[i].x; - occludedUmbra[i].y = poly[i].y; - } - - // Both umbra and incoming polygon are guaranteed to be CW, so we can call - // intersection() directly. - return intersection(umbra, umbraLength, - occludedUmbra, polyLength); -} - -/** * This is only for experimental purpose. * After intersections are calculated, we could smooth the polygon if needed. * So far, we don't think it is more appealing yet. @@ -889,490 +638,233 @@ void SpotShadow::smoothPolygon(int level, int rays, float* rayDist) { } } -/** - * Generate a array of the angleData for either umbra or penumbra vertices. - * - * This array will be merged and used to guide where to shoot the rays, in clockwise order. - * - * @param angleDataList The result array of angle data. - * - * @return int The maximum angle's index in the array. - */ -int SpotShadow::setupAngleList(VertexAngleData* angleDataList, - int polyLength, const Vector2* polygon, const Vector2& centroid, - bool isPenumbra, const char* name) { - float maxAngle = FLT_MIN; - int maxAngleIndex = 0; - for (int i = 0; i < polyLength; i++) { - float currentAngle = angle(polygon[i], centroid); - if (currentAngle > maxAngle) { - maxAngle = currentAngle; - maxAngleIndex = i; - } - angleDataList[i].set(currentAngle, isPenumbra, i); -#if DEBUG_SHADOW - ALOGD("%s AngleList i %d %f", name, i, currentAngle); -#endif - } - return maxAngleIndex; -} +// Index pair is meant for storing the tessellation information for the penumbra +// area. One index must come from exterior tangent of the circles, the other one +// must come from the interior tangent of the circles. +struct IndexPair { + int outerIndex; + int innerIndex; +}; -/** - * Make sure the polygons are indeed in clockwise order. - * - * Possible reasons to return false: 1. The input polygon is not setup properly. 2. The hull - * algorithm is not able to generate it properly. - * - * Anyway, since the algorithm depends on the clockwise, when these kind of unexpected error - * situation is found, we need to detect it and early return without corrupting the memory. - * - * @return bool True if the angle list is actually from big to small. - */ -bool SpotShadow::checkClockwise(int indexOfMaxAngle, int listLength, VertexAngleData* angleList, - const char* name) { - int currentIndex = indexOfMaxAngle; -#if DEBUG_SHADOW - ALOGD("max index %d", currentIndex); -#endif - for (int i = 0; i < listLength - 1; i++) { - // TODO: Cache the last angle. - float currentAngle = angleList[currentIndex].mAngle; - float nextAngle = angleList[(currentIndex + 1) % listLength].mAngle; - if (currentAngle < nextAngle) { -#if DEBUG_SHADOW - ALOGE("%s, is not CW, at index %d", name, currentIndex); -#endif - return false; +// For one penumbra vertex, find the cloest umbra vertex and return its index. +inline int getClosestUmbraIndex(const Vector2& pivot, const Vector2* polygon, int polygonLength) { + float minLengthSquared = FLT_MAX; + int resultIndex = -1; + bool hasDecreased = false; + // Starting with some negative offset, assuming both umbra and penumbra are starting + // at the same angle, this can help to find the result faster. + // Normally, loop 3 times, we can find the closest point. + int offset = polygonLength - 2; + for (int i = 0; i < polygonLength; i++) { + int currentIndex = (i + offset) % polygonLength; + float currentLengthSquared = (pivot - polygon[currentIndex]).lengthSquared(); + if (currentLengthSquared < minLengthSquared) { + if (minLengthSquared != FLT_MAX) { + hasDecreased = true; + } + minLengthSquared = currentLengthSquared; + resultIndex = currentIndex; + } else if (currentLengthSquared > minLengthSquared && hasDecreased) { + // Early break b/c we have found the closet one and now the length + // is increasing again. + break; } - currentIndex = (currentIndex + 1) % listLength; } - return true; + if(resultIndex == -1) { + ALOGE("resultIndex is -1, the polygon must be invalid!"); + resultIndex = 0; + } + return resultIndex; } -/** - * Check the polygon is clockwise. - * - * @return bool True is the polygon is clockwise. - */ -bool SpotShadow::checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex, - const float* polyAngleList) { - bool isPolyCW = true; - // Starting from maxPolyAngleIndex , check around to make sure angle decrease. - for (int i = 0; i < polyAngleLength - 1; i++) { - float currentAngle = polyAngleList[(i + maxPolyAngleIndex) % polyAngleLength]; - float nextAngle = polyAngleList[(i + maxPolyAngleIndex + 1) % polyAngleLength]; - if (currentAngle < nextAngle) { - isPolyCW = false; - } +// Allow some epsilon here since the later ray intersection did allow for some small +// floating point error, when the intersection point is slightly outside the segment. +inline bool sameDirections(bool isPositiveCross, float a, float b) { + if (isPositiveCross) { + return a >= -EPSILON && b >= -EPSILON; + } else { + return a <= EPSILON && b <= EPSILON; } - return isPolyCW; } -/** - * Given the sorted array of all the vertices angle data, calculate for each - * vertices, the offset value to array element which represent the start edge - * of the polygon we need to shoot the ray at. - * - * TODO: Calculate this for umbra and penumbra in one loop using one single array. - * - * @param distances The result of the array distance counter. - */ -void SpotShadow::calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength, - const VertexAngleData* allVerticesAngleData, int* distances) { - - bool firstVertexIsPenumbra = allVerticesAngleData[0].mIsPenumbra; - // If we want distance to inner, then we just set to 0 when we see inner. - bool needsSearch = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra; - int distanceCounter = 0; - if (needsSearch) { - int foundIndex = -1; - for (int i = (angleLength - 1); i >= 0; i--) { - bool currentIsOuter = allVerticesAngleData[i].mIsPenumbra; - // If we need distance to inner, then we need to find a inner vertex. - if (currentIsOuter != firstVertexIsPenumbra) { - foundIndex = i; - break; - } - } - LOG_ALWAYS_FATAL_IF(foundIndex == -1, "Wrong index found, means either" - " umbra or penumbra's length is 0"); - distanceCounter = angleLength - foundIndex; - } +// Find the right polygon edge to shoot the ray at. +inline int findPolyIndex(bool isPositiveCross, int startPolyIndex, const Vector2& umbraDir, + const Vector2* polyToCentroid, int polyLength) { + // Make sure we loop with a bound. + for (int i = 0; i < polyLength; i++) { + int currentIndex = (i + startPolyIndex) % polyLength; + const Vector2& currentToCentroid = polyToCentroid[currentIndex]; + const Vector2& nextToCentroid = polyToCentroid[(currentIndex + 1) % polyLength]; + + float currentCrossUmbra = currentToCentroid.cross(umbraDir); + float umbraCrossNext = umbraDir.cross(nextToCentroid); + if (sameDirections(isPositiveCross, currentCrossUmbra, umbraCrossNext)) { #if DEBUG_SHADOW - ALOGD("distances[0] is %d", distanceCounter); + ALOGD("findPolyIndex loop %d times , index %d", i, currentIndex ); #endif - - distances[0] = distanceCounter; // means never see a target poly - - for (int i = 1; i < angleLength; i++) { - bool firstVertexIsPenumbra = allVerticesAngleData[i].mIsPenumbra; - // When we needs for distance for each outer vertex to inner, then we - // increase the distance when seeing outer vertices. Otherwise, we clear - // to 0. - bool needsIncrement = needsOffsetToUmbra ? firstVertexIsPenumbra : !firstVertexIsPenumbra; - // If counter is not -1, that means we have seen an other polygon's vertex. - if (needsIncrement && distanceCounter != -1) { - distanceCounter++; - } else { - distanceCounter = 0; + return currentIndex; } - distances[i] = distanceCounter; } + LOG_ALWAYS_FATAL("Can't find the right polygon's edge from startPolyIndex %d", startPolyIndex); + return -1; } -/** - * Given umbra and penumbra angle data list, merge them by sorting the angle - * from the biggest to smallest. - * - * @param allVerticesAngleData The result array of merged angle data. - */ -void SpotShadow::mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex, - const VertexAngleData* umbraAngleList, int umbraLength, - const VertexAngleData* penumbraAngleList, int penumbraLength, - VertexAngleData* allVerticesAngleData) { - - int totalRayNumber = umbraLength + penumbraLength; - int umbraIndex = maxUmbraAngleIndex; - int penumbraIndex = maxPenumbraAngleIndex; - - float currentUmbraAngle = umbraAngleList[umbraIndex].mAngle; - float currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle; - - // TODO: Clean this up using a while loop with 2 iterators. - for (int i = 0; i < totalRayNumber; i++) { - if (currentUmbraAngle > currentPenumbraAngle) { - allVerticesAngleData[i] = umbraAngleList[umbraIndex]; - umbraIndex = (umbraIndex + 1) % umbraLength; - - // If umbraIndex round back, that means we are running out of - // umbra vertices to merge, so just copy all the penumbra leftover. - // Otherwise, we update the currentUmbraAngle. - if (umbraIndex != maxUmbraAngleIndex) { - currentUmbraAngle = umbraAngleList[umbraIndex].mAngle; - } else { - for (int j = i + 1; j < totalRayNumber; j++) { - allVerticesAngleData[j] = penumbraAngleList[penumbraIndex]; - penumbraIndex = (penumbraIndex + 1) % penumbraLength; - } +// Generate the index pair for penumbra / umbra vertices, and more penumbra vertices +// if needed. +inline void genNewPenumbraAndPairWithUmbra(const Vector2* penumbra, int penumbraLength, + const Vector2* umbra, int umbraLength, Vector2* newPenumbra, int& newPenumbraIndex, + IndexPair* verticesPair, int& verticesPairIndex) { + // In order to keep everything in just one loop, we need to pre-compute the + // closest umbra vertex for the last penumbra vertex. + int previousClosestUmbraIndex = getClosestUmbraIndex(penumbra[penumbraLength - 1], + umbra, umbraLength); + for (int i = 0; i < penumbraLength; i++) { + const Vector2& currentPenumbraVertex = penumbra[i]; + // For current penumbra vertex, starting from previousClosestUmbraIndex, + // then check the next one until the distance increase. + // The last one before the increase is the umbra vertex we need to pair with. + float currentLengthSquared = + (currentPenumbraVertex - umbra[previousClosestUmbraIndex]).lengthSquared(); + int currentClosestUmbraIndex = previousClosestUmbraIndex; + int indexDelta = 0; + for (int j = 1; j < umbraLength; j++) { + int newUmbraIndex = (previousClosestUmbraIndex + j) % umbraLength; + float newLengthSquared = (currentPenumbraVertex - umbra[newUmbraIndex]).lengthSquared(); + if (newLengthSquared > currentLengthSquared) { + // currentClosestUmbraIndex is the umbra vertex's index which has + // currently found smallest distance, so we can simply break here. break; - } - } else { - allVerticesAngleData[i] = penumbraAngleList[penumbraIndex]; - penumbraIndex = (penumbraIndex + 1) % penumbraLength; - // If penumbraIndex round back, that means we are running out of - // penumbra vertices to merge, so just copy all the umbra leftover. - // Otherwise, we update the currentPenumbraAngle. - if (penumbraIndex != maxPenumbraAngleIndex) { - currentPenumbraAngle = penumbraAngleList[penumbraIndex].mAngle; } else { - for (int j = i + 1; j < totalRayNumber; j++) { - allVerticesAngleData[j] = umbraAngleList[umbraIndex]; - umbraIndex = (umbraIndex + 1) % umbraLength; - } - break; + currentLengthSquared = newLengthSquared; + indexDelta++; + currentClosestUmbraIndex = newUmbraIndex; } } - } -} -#if DEBUG_SHADOW -/** - * DEBUG ONLY: Verify all the offset compuation is correctly done by examining - * each vertex and its neighbor. - */ -static void verifyDistanceCounter(const VertexAngleData* allVerticesAngleData, - const int* distances, int angleLength, const char* name) { - int currentDistance = distances[0]; - for (int i = 1; i < angleLength; i++) { - if (distances[i] != INT_MIN) { - if (!((currentDistance + 1) == distances[i] - || distances[i] == 0)) { - ALOGE("Wrong distance found at i %d name %s", i, name); - } - currentDistance = distances[i]; - if (currentDistance != 0) { - bool currentOuter = allVerticesAngleData[i].mIsPenumbra; - for (int j = 1; j <= (currentDistance - 1); j++) { - bool neigborOuter = - allVerticesAngleData[(i + angleLength - j) % angleLength].mIsPenumbra; - if (neigborOuter != currentOuter) { - ALOGE("Wrong distance found at i %d name %s", i, name); - } - } - bool oppositeOuter = - allVerticesAngleData[(i + angleLength - currentDistance) % angleLength].mIsPenumbra; - if (oppositeOuter == currentOuter) { - ALOGE("Wrong distance found at i %d name %s", i, name); - } + if (indexDelta > 1) { + // For those umbra don't have penumbra, generate new penumbra vertices by interpolation. + // + // Assuming Pi for penumbra vertices, and Ui for umbra vertices. + // In the case like below P1 paired with U1 and P2 paired with U5. + // U2 to U4 are unpaired umbra vertices. + // + // P1 P2 + // | | + // U1 U2 U3 U4 U5 + // + // We will need to generate 3 more penumbra vertices P1.1, P1.2, P1.3 + // to pair with U2 to U4. + // + // P1 P1.1 P1.2 P1.3 P2 + // | | | | | + // U1 U2 U3 U4 U5 + // + // That distance ratio b/t Ui to U1 and Ui to U5 decides its paired penumbra + // vertex's location. + int newPenumbraNumber = indexDelta - 1; + + float accumulatedDeltaLength[newPenumbraNumber]; + float totalDeltaLength = 0; + + // To save time, cache the previous umbra vertex info outside the loop + // and update each loop. + Vector2 previousClosestUmbra = umbra[previousClosestUmbraIndex]; + Vector2 skippedUmbra; + // Use umbra data to precompute the length b/t unpaired umbra vertices, + // and its ratio against the total length. + for (int k = 0; k < indexDelta; k++) { + int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength; + skippedUmbra = umbra[skippedUmbraIndex]; + float currentDeltaLength = (skippedUmbra - previousClosestUmbra).length(); + + totalDeltaLength += currentDeltaLength; + accumulatedDeltaLength[k] = totalDeltaLength; + + previousClosestUmbra = skippedUmbra; } - } - } -} -/** - * DEBUG ONLY: Verify all the angle data compuated are is correctly done - */ -static void verifyAngleData(int totalRayNumber, const VertexAngleData* allVerticesAngleData, - const int* distancesToInner, const int* distancesToOuter, - const VertexAngleData* umbraAngleList, int maxUmbraAngleIndex, int umbraLength, - const VertexAngleData* penumbraAngleList, int maxPenumbraAngleIndex, - int penumbraLength) { - for (int i = 0; i < totalRayNumber; i++) { - ALOGD("currentAngleList i %d, angle %f, isInner %d, index %d distancesToInner" - " %d distancesToOuter %d", i, allVerticesAngleData[i].mAngle, - !allVerticesAngleData[i].mIsPenumbra, - allVerticesAngleData[i].mVertexIndex, distancesToInner[i], distancesToOuter[i]); - } + const Vector2& previousPenumbra = penumbra[(i + penumbraLength - 1) % penumbraLength]; + // Then for each unpaired umbra vertex, create a new penumbra by the ratio, + // and pair them togehter. + for (int k = 0; k < newPenumbraNumber; k++) { + float weightForCurrentPenumbra = 1.0f; + if (totalDeltaLength != 0.0f) { + weightForCurrentPenumbra = accumulatedDeltaLength[k] / totalDeltaLength; + } + float weightForPreviousPenumbra = 1.0f - weightForCurrentPenumbra; - verifyDistanceCounter(allVerticesAngleData, distancesToInner, totalRayNumber, "distancesToInner"); - verifyDistanceCounter(allVerticesAngleData, distancesToOuter, totalRayNumber, "distancesToOuter"); + Vector2 interpolatedPenumbra = currentPenumbraVertex * weightForCurrentPenumbra + + previousPenumbra * weightForPreviousPenumbra; - for (int i = 0; i < totalRayNumber; i++) { - if ((distancesToInner[i] * distancesToOuter[i]) != 0) { - ALOGE("distancesToInner wrong at index %d distancesToInner[i] %d," - " distancesToOuter[i] %d", i, distancesToInner[i], distancesToOuter[i]); - } - } - int currentUmbraVertexIndex = - umbraAngleList[maxUmbraAngleIndex].mVertexIndex; - int currentPenumbraVertexIndex = - penumbraAngleList[maxPenumbraAngleIndex].mVertexIndex; - for (int i = 0; i < totalRayNumber; i++) { - if (allVerticesAngleData[i].mIsPenumbra == true) { - if (allVerticesAngleData[i].mVertexIndex != currentPenumbraVertexIndex) { - ALOGW("wrong penumbra indexing i %d allVerticesAngleData[i].mVertexIndex %d " - "currentpenumbraVertexIndex %d", i, - allVerticesAngleData[i].mVertexIndex, currentPenumbraVertexIndex); - } - currentPenumbraVertexIndex = (currentPenumbraVertexIndex + 1) % penumbraLength; - } else { - if (allVerticesAngleData[i].mVertexIndex != currentUmbraVertexIndex) { - ALOGW("wrong umbra indexing i %d allVerticesAngleData[i].mVertexIndex %d " - "currentUmbraVertexIndex %d", i, - allVerticesAngleData[i].mVertexIndex, currentUmbraVertexIndex); + int skippedUmbraIndex = (previousClosestUmbraIndex + k + 1) % umbraLength; + verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex; + verticesPair[verticesPairIndex].innerIndex = skippedUmbraIndex; + verticesPairIndex++; + newPenumbra[newPenumbraIndex++] = interpolatedPenumbra; } - currentUmbraVertexIndex = (currentUmbraVertexIndex + 1) % umbraLength; - } - } - for (int i = 0; i < totalRayNumber - 1; i++) { - float currentAngle = allVerticesAngleData[i].mAngle; - float nextAngle = allVerticesAngleData[(i + 1) % totalRayNumber].mAngle; - if (currentAngle < nextAngle) { - ALOGE("Unexpected angle values!, currentAngle nextAngle %f %f", currentAngle, nextAngle); } + verticesPair[verticesPairIndex].outerIndex = newPenumbraIndex; + verticesPair[verticesPairIndex].innerIndex = currentClosestUmbraIndex; + verticesPairIndex++; + newPenumbra[newPenumbraIndex++] = currentPenumbraVertex; + + previousClosestUmbraIndex = currentClosestUmbraIndex; } } -#endif -/** - * In order to compute the occluded umbra, we need to setup the angle data list - * for the polygon data. Since we only store one poly vertex per polygon vertex, - * this array only needs to be a float array which are the angles for each vertex. - * - * @param polyAngleList The result list - * - * @return int The index for the maximum angle in this array. - */ -int SpotShadow::setupPolyAngleList(float* polyAngleList, int polyAngleLength, - const Vector2* poly2d, const Vector2& centroid) { - int maxPolyAngleIndex = -1; - float maxPolyAngle = -FLT_MAX; - for (int i = 0; i < polyAngleLength; i++) { - polyAngleList[i] = angle(poly2d[i], centroid); - if (polyAngleList[i] > maxPolyAngle) { - maxPolyAngle = polyAngleList[i]; - maxPolyAngleIndex = i; +// Precompute all the polygon's vector, return true if the reference cross product is positive. +inline bool genPolyToCentroid(const Vector2* poly2d, int polyLength, + const Vector2& centroid, Vector2* polyToCentroid) { + for (int j = 0; j < polyLength; j++) { + polyToCentroid[j] = poly2d[j] - centroid; + // Normalize these vectors such that we can use epsilon comparison after + // computing their cross products with another normalized vector. + polyToCentroid[j].normalize(); + } + float refCrossProduct = 0; + for (int j = 0; j < polyLength; j++) { + refCrossProduct = polyToCentroid[j].cross(polyToCentroid[(j + 1) % polyLength]); + if (refCrossProduct != 0) { + break; } } - return maxPolyAngleIndex; -} -/** - * For umbra and penumbra, given the offset info and the current ray number, - * find the right edge index (the (starting vertex) for the ray to shoot at. - * - * @return int The index of the starting vertex of the edge. - */ -inline int SpotShadow::getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber, - const VertexAngleData* allVerticesAngleData) { - int tempOffset = offsets[rayIndex]; - int targetRayIndex = (rayIndex - tempOffset + totalRayNumber) % totalRayNumber; - return allVerticesAngleData[targetRayIndex].mVertexIndex; + return refCrossProduct > 0; } -/** - * For the occluded umbra, given the array of angles, find the index of the - * starting vertex of the edge, for the ray to shoo at. - * - * TODO: Save the last result to shorten the search distance. - * - * @return int The index of the starting vertex of the edge. - */ -inline int SpotShadow::getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength, - const float* polyAngleList, float rayAngle) { - int minPolyAngleIndex = (maxPolyAngleIndex + polyLength - 1) % polyLength; - int resultIndex = -1; - if (rayAngle > polyAngleList[maxPolyAngleIndex] - || rayAngle <= polyAngleList[minPolyAngleIndex]) { - resultIndex = minPolyAngleIndex; +// For one umbra vertex, shoot an ray from centroid to it. +// If the ray hit the polygon first, then return the intersection point as the +// closer vertex. +inline Vector2 getCloserVertex(const Vector2& umbraVertex, const Vector2& centroid, + const Vector2* poly2d, int polyLength, const Vector2* polyToCentroid, + bool isPositiveCross, int& previousPolyIndex) { + Vector2 umbraToCentroid = umbraVertex - centroid; + float distanceToUmbra = umbraToCentroid.length(); + umbraToCentroid = umbraToCentroid / distanceToUmbra; + + // previousPolyIndex is updated for each item such that we can minimize the + // looping inside findPolyIndex(); + previousPolyIndex = findPolyIndex(isPositiveCross, previousPolyIndex, + umbraToCentroid, polyToCentroid, polyLength); + + float dx = umbraToCentroid.x; + float dy = umbraToCentroid.y; + float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy, + poly2d[previousPolyIndex], poly2d[(previousPolyIndex + 1) % polyLength]); + if (distanceToIntersectPoly < 0) { + distanceToIntersectPoly = 0; + } + + // Pick the closer one as the occluded area vertex. + Vector2 closerVertex; + if (distanceToIntersectPoly < distanceToUmbra) { + closerVertex.x = centroid.x + dx * distanceToIntersectPoly; + closerVertex.y = centroid.y + dy * distanceToIntersectPoly; } else { - for (int i = 0; i < polyLength - 1; i++) { - int currentIndex = (maxPolyAngleIndex + i) % polyLength; - int nextIndex = (maxPolyAngleIndex + i + 1) % polyLength; - if (rayAngle <= polyAngleList[currentIndex] - && rayAngle > polyAngleList[nextIndex]) { - resultIndex = currentIndex; - } - } + closerVertex = umbraVertex; } - if (CC_UNLIKELY(resultIndex == -1)) { - // TODO: Add more error handling here. - ALOGE("Wrong index found, means no edge can't be found for rayAngle %f", rayAngle); - } - return resultIndex; -} - -/** - * Convert the incoming polygons into arrays of vertices, for each ray. - * Ray only shoots when there is one vertex either on penumbra on umbra. - * - * Finally, it will generate vertices per ray for umbra, penumbra and optionally - * occludedUmbra. - * - * Return true (success) when all vertices are generated - */ -int SpotShadow::convertPolysToVerticesPerRay( - bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength, - const Vector2* umbra, int umbraLength, const Vector2* penumbra, - int penumbraLength, const Vector2& centroid, - Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay, - Vector2* occludedUmbraVerticesPerRay) { - int totalRayNumber = umbraLength + penumbraLength; - - // For incoming umbra / penumbra polygons, we will build an intermediate data - // structure to help us sort all the vertices according to the vertices. - // Using this data structure, we can tell where (the angle) to shoot the ray, - // whether we shoot at penumbra edge or umbra edge, and which edge to shoot at. - // - // We first parse each vertices and generate a table of VertexAngleData. - // Based on that, we create 2 arrays telling us which edge to shoot at. - VertexAngleData allVerticesAngleData[totalRayNumber]; - VertexAngleData umbraAngleList[umbraLength]; - VertexAngleData penumbraAngleList[penumbraLength]; - - int polyAngleLength = hasOccludedUmbraArea ? polyLength : 0; - float polyAngleList[polyAngleLength]; - - const int maxUmbraAngleIndex = - setupAngleList(umbraAngleList, umbraLength, umbra, centroid, false, "umbra"); - const int maxPenumbraAngleIndex = - setupAngleList(penumbraAngleList, penumbraLength, penumbra, centroid, true, "penumbra"); - const int maxPolyAngleIndex = setupPolyAngleList(polyAngleList, polyAngleLength, poly2d, centroid); - - // Check all the polygons here are CW. - bool isPolyCW = checkPolyClockwise(polyAngleLength, maxPolyAngleIndex, polyAngleList); - bool isUmbraCW = checkClockwise(maxUmbraAngleIndex, umbraLength, - umbraAngleList, "umbra"); - bool isPenumbraCW = checkClockwise(maxPenumbraAngleIndex, penumbraLength, - penumbraAngleList, "penumbra"); - - if (!isUmbraCW || !isPenumbraCW || !isPolyCW) { -#if DEBUG_SHADOW - ALOGE("One polygon is not CW isUmbraCW %d isPenumbraCW %d isPolyCW %d", - isUmbraCW, isPenumbraCW, isPolyCW); -#endif - return false; - } - - mergeAngleList(maxUmbraAngleIndex, maxPenumbraAngleIndex, - umbraAngleList, umbraLength, penumbraAngleList, penumbraLength, - allVerticesAngleData); - - // Calculate the offset to the left most Inner vertex for each outerVertex. - // Then the offset to the left most Outer vertex for each innerVertex. - int offsetToInner[totalRayNumber]; - int offsetToOuter[totalRayNumber]; - calculateDistanceCounter(true, totalRayNumber, allVerticesAngleData, offsetToInner); - calculateDistanceCounter(false, totalRayNumber, allVerticesAngleData, offsetToOuter); - - // Generate both umbraVerticesPerRay and penumbraVerticesPerRay - for (int i = 0; i < totalRayNumber; i++) { - float rayAngle = allVerticesAngleData[i].mAngle; - bool isUmbraVertex = !allVerticesAngleData[i].mIsPenumbra; - - float dx = cosf(rayAngle); - float dy = sinf(rayAngle); - float distanceToIntersectUmbra = -1; - - if (isUmbraVertex) { - // We can just copy umbra easily, and calculate the distance for the - // occluded umbra computation. - int startUmbraIndex = allVerticesAngleData[i].mVertexIndex; - umbraVerticesPerRay[i] = umbra[startUmbraIndex]; - if (hasOccludedUmbraArea) { - distanceToIntersectUmbra = (umbraVerticesPerRay[i] - centroid).length(); - } - - //shoot ray to penumbra only - int startPenumbraIndex = getEdgeStartIndex(offsetToOuter, i, totalRayNumber, - allVerticesAngleData); - float distanceToIntersectPenumbra = rayIntersectPoints(centroid, dx, dy, - penumbra[startPenumbraIndex], - penumbra[(startPenumbraIndex + 1) % penumbraLength]); - if (distanceToIntersectPenumbra < 0) { -#if DEBUG_SHADOW - ALOGW("convertPolyToRayDist for penumbra failed rayAngle %f dx %f dy %f", - rayAngle, dx, dy); -#endif - distanceToIntersectPenumbra = 0; - } - penumbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPenumbra; - penumbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPenumbra; - } else { - // We can just copy the penumbra - int startPenumbraIndex = allVerticesAngleData[i].mVertexIndex; - penumbraVerticesPerRay[i] = penumbra[startPenumbraIndex]; - - // And shoot ray to umbra only - int startUmbraIndex = getEdgeStartIndex(offsetToInner, i, totalRayNumber, - allVerticesAngleData); - - distanceToIntersectUmbra = rayIntersectPoints(centroid, dx, dy, - umbra[startUmbraIndex], umbra[(startUmbraIndex + 1) % umbraLength]); - if (distanceToIntersectUmbra < 0) { -#if DEBUG_SHADOW - ALOGW("convertPolyToRayDist for umbra failed rayAngle %f dx %f dy %f", - rayAngle, dx, dy); -#endif - distanceToIntersectUmbra = 0; - } - umbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectUmbra; - umbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectUmbra; - } - - if (hasOccludedUmbraArea) { - // Shoot the same ray to the poly2d, and get the distance. - int startPolyIndex = getPolyEdgeStartIndex(maxPolyAngleIndex, polyLength, - polyAngleList, rayAngle); - - float distanceToIntersectPoly = rayIntersectPoints(centroid, dx, dy, - poly2d[startPolyIndex], poly2d[(startPolyIndex + 1) % polyLength]); - if (distanceToIntersectPoly < 0) { - distanceToIntersectPoly = 0; - } - distanceToIntersectPoly = MathUtils::min(distanceToIntersectUmbra, distanceToIntersectPoly); - occludedUmbraVerticesPerRay[i].x = centroid.x + dx * distanceToIntersectPoly; - occludedUmbraVerticesPerRay[i].y = centroid.y + dy * distanceToIntersectPoly; - } - } - -#if DEBUG_SHADOW - verifyAngleData(totalRayNumber, allVerticesAngleData, offsetToInner, - offsetToOuter, umbraAngleList, maxUmbraAngleIndex, umbraLength, - penumbraAngleList, maxPenumbraAngleIndex, penumbraLength); -#endif - return true; // success + return closerVertex; } /** @@ -1382,7 +874,6 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength, const Vector3* poly, int polyLength, VertexBuffer& shadowTriangleStrip, const Vector2& centroid) { - bool hasOccludedUmbraArea = false; Vector2 poly2d[polyLength]; @@ -1398,128 +889,140 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength } } - int totalRayNum = umbraLength + penumbraLength; - Vector2 umbraVertices[totalRayNum]; - Vector2 penumbraVertices[totalRayNum]; - Vector2 occludedUmbraVertices[totalRayNum]; - bool convertSuccess = convertPolysToVerticesPerRay(hasOccludedUmbraArea, poly2d, - polyLength, umbra, umbraLength, penumbra, penumbraLength, - centroid, umbraVertices, penumbraVertices, occludedUmbraVertices); - if (!convertSuccess) { - return; + // For each penumbra vertex, find its corresponding closest umbra vertex index. + // + // Penumbra Vertices marked as Pi + // Umbra Vertices marked as Ui + // (P3) + // (P2) | ' (P4) + // (P1)' | | ' + // ' | | ' + // (P0) ------------------------------------------------(P5) + // | (U0) |(U1) + // | | + // | |(U2) (P5.1) + // | | + // | | + // | | + // | | + // | | + // | | + // (U4)-----------------------------------(U3) (P6) + // + // At least, like P0, P1, P2, they will find the matching umbra as U0. + // If we jump over some umbra vertex without matching penumbra vertex, then + // we will generate some new penumbra vertex by interpolation. Like P6 is + // matching U3, but U2 is not matched with any penumbra vertex. + // So interpolate P5.1 out and match U2. + // In this way, every umbra vertex will have a matching penumbra vertex. + // + // The total pair number can be as high as umbraLength + penumbraLength. + const int maxNewPenumbraLength = umbraLength + penumbraLength; + IndexPair verticesPair[maxNewPenumbraLength]; + int verticesPairIndex = 0; + + // Cache all the existing penumbra vertices and newly interpolated vertices into a + // a new array. + Vector2 newPenumbra[maxNewPenumbraLength]; + int newPenumbraIndex = 0; + + // For each penumbra vertex, find its closet umbra vertex by comparing the + // neighbor umbra vertices. + genNewPenumbraAndPairWithUmbra(penumbra, penumbraLength, umbra, umbraLength, newPenumbra, + newPenumbraIndex, verticesPair, verticesPairIndex); + ShadowTessellator::checkOverflow(verticesPairIndex, maxNewPenumbraLength, "Spot pair"); + ShadowTessellator::checkOverflow(newPenumbraIndex, maxNewPenumbraLength, "Spot new penumbra"); +#if DEBUG_SHADOW + for (int i = 0; i < umbraLength; i++) { + ALOGD("umbra i %d, [%f, %f]", i, umbra[i].x, umbra[i].y); + } + for (int i = 0; i < newPenumbraIndex; i++) { + ALOGD("new penumbra i %d, [%f, %f]", i, newPenumbra[i].x, newPenumbra[i].y); } + for (int i = 0; i < verticesPairIndex; i++) { + ALOGD("index i %d, [%d, %d]", i, verticesPair[i].outerIndex, verticesPair[i].innerIndex); + } +#endif - // Minimal value is 1, for each vertex show up once. - // The bigger this value is , the smoother the look is, but more memory - // is consumed. - // When the ray number is high, that means the polygon has been fine - // tessellated, we don't need this extra slice, just keep it as 1. - int sliceNumberPerEdge = (totalRayNum > FINE_TESSELLATED_POLYGON_RAY_NUMBER) ? 1 : 2; - - // For each polygon, we at most add (totalRayNum * sliceNumberPerEdge) vertices. - int slicedVertexCountPerPolygon = totalRayNum * sliceNumberPerEdge; - int totalVertexCount = slicedVertexCountPerPolygon * 2 + totalRayNum; - int totalIndexCount = 2 * (slicedVertexCountPerPolygon * 2 + 2); + // For the size of vertex buffer, we need 3 rings, one has newPenumbraSize, + // one has umbraLength, the last one has at most umbraLength. + // + // For the size of index buffer, the umbra area needs (2 * umbraLength + 2). + // The penumbra one can vary a bit, but it is bounded by (2 * verticesPairIndex + 2). + // And 2 more for jumping between penumbra to umbra. + const int newPenumbraLength = newPenumbraIndex; + const int totalVertexCount = newPenumbraLength + umbraLength * 2; + const int totalIndexCount = 2 * umbraLength + 2 * verticesPairIndex + 6; AlphaVertex* shadowVertices = shadowTriangleStrip.alloc<AlphaVertex>(totalVertexCount); uint16_t* indexBuffer = shadowTriangleStrip.allocIndices<uint16_t>(totalIndexCount); - - int indexBufferIndex = 0; int vertexBufferIndex = 0; + int indexBufferIndex = 0; - uint16_t slicedUmbraVertexIndex[totalRayNum * sliceNumberPerEdge]; - // Should be something like 0 0 0 1 1 1 2 3 3 3... - int rayNumberPerSlicedUmbra[totalRayNum * sliceNumberPerEdge]; - int realUmbraVertexCount = 0; - for (int i = 0; i < totalRayNum; i++) { - Vector2 currentPenumbra = penumbraVertices[i]; - Vector2 currentUmbra = umbraVertices[i]; - - Vector2 nextPenumbra = penumbraVertices[(i + 1) % totalRayNum]; - Vector2 nextUmbra = umbraVertices[(i + 1) % totalRayNum]; - // NextUmbra/Penumbra will be done in the next loop!! - for (int weight = 0; weight < sliceNumberPerEdge; weight++) { - const Vector2& slicedPenumbra = (currentPenumbra * (sliceNumberPerEdge - weight) - + nextPenumbra * weight) / sliceNumberPerEdge; - - const Vector2& slicedUmbra = (currentUmbra * (sliceNumberPerEdge - weight) - + nextUmbra * weight) / sliceNumberPerEdge; - - // In the vertex buffer, we fill the Penumbra first, then umbra. - indexBuffer[indexBufferIndex++] = vertexBufferIndex; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedPenumbra.x, - slicedPenumbra.y, 0.0f); + // Fill the IB and VB for the penumbra area. + for (int i = 0; i < newPenumbraLength; i++) { + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, + newPenumbra[i].y, 0.0f); + } + for (int i = 0; i < umbraLength; i++) { + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y, + M_PI); + } - // When we add umbra vertex, we need to remember its current ray number. - // And its own vertexBufferIndex. This is for occluded umbra usage. - indexBuffer[indexBufferIndex++] = vertexBufferIndex; - rayNumberPerSlicedUmbra[realUmbraVertexCount] = i; - slicedUmbraVertexIndex[realUmbraVertexCount] = vertexBufferIndex; - realUmbraVertexCount++; - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], slicedUmbra.x, - slicedUmbra.y, M_PI); - } + for (int i = 0; i < verticesPairIndex; i++) { + indexBuffer[indexBufferIndex++] = verticesPair[i].outerIndex; + // All umbra index need to be offseted by newPenumbraSize. + indexBuffer[indexBufferIndex++] = verticesPair[i].innerIndex + newPenumbraLength; } + indexBuffer[indexBufferIndex++] = verticesPair[0].outerIndex; + indexBuffer[indexBufferIndex++] = verticesPair[0].innerIndex + newPenumbraLength; - indexBuffer[indexBufferIndex++] = 0; - //RealUmbraVertexIndex[0] must be 1, so we connect back well at the - //beginning of occluded area. - indexBuffer[indexBufferIndex++] = 1; + // Now fill the IB and VB for the umbra area. + // First duplicated the index from previous strip and the first one for the + // degenerated triangles. + indexBuffer[indexBufferIndex] = indexBuffer[indexBufferIndex - 1]; + indexBufferIndex++; + indexBuffer[indexBufferIndex++] = newPenumbraLength + 0; + // Save the first VB index for umbra area in order to close the loop. + int savedStartIndex = vertexBufferIndex; - float occludedUmbraAlpha = M_PI; if (hasOccludedUmbraArea) { - // Now the occludedUmbra area; - int currentRayNumber = -1; - int firstOccludedUmbraIndex = -1; - for (int i = 0; i < realUmbraVertexCount; i++) { - indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i]; - - // If the occludedUmbra vertex has not been added yet, then add it. - // Otherwise, just use the previously added occludedUmbra vertices. - if (rayNumberPerSlicedUmbra[i] != currentRayNumber) { - currentRayNumber++; - indexBuffer[indexBufferIndex++] = vertexBufferIndex; - // We need to remember the begining of the occludedUmbra vertices - // to close this loop. - if (currentRayNumber == 0) { - firstOccludedUmbraIndex = vertexBufferIndex; - } - AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - occludedUmbraVertices[currentRayNumber].x, - occludedUmbraVertices[currentRayNumber].y, - occludedUmbraAlpha); - } else { - indexBuffer[indexBufferIndex++] = (vertexBufferIndex - 1); - } + // Precompute all the polygon's vector, and the reference cross product, + // in order to find the right polygon edge for the ray to intersect. + Vector2 polyToCentroid[polyLength]; + bool isPositiveCross = genPolyToCentroid(poly2d, polyLength, centroid, polyToCentroid); + + // Because both the umbra and polygon are going in the same direction, + // we can save the previous polygon index to make sure we have less polygon + // vertex to compute for each ray. + int previousPolyIndex = 0; + for (int i = 0; i < umbraLength; i++) { + // Shoot a ray from centroid to each umbra vertices and pick the one with + // shorter distance to the centroid, b/t the umbra vertex or the intersection point. + Vector2 closerVertex = getCloserVertex(umbra[i], centroid, poly2d, polyLength, + polyToCentroid, isPositiveCross, previousPolyIndex); + + // We already stored the umbra vertices, just need to add the occlued umbra's ones. + indexBuffer[indexBufferIndex++] = newPenumbraLength + i; + indexBuffer[indexBufferIndex++] = vertexBufferIndex; + AlphaVertex::set(&shadowVertices[vertexBufferIndex++], + closerVertex.x, closerVertex.y, M_PI); } - // Close the loop here! - indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0]; - indexBuffer[indexBufferIndex++] = firstOccludedUmbraIndex; } else { + // If there is no occluded umbra at all, then draw the triangle fan + // starting from the centroid to all umbra vertices. int lastCentroidIndex = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], centroid.x, - centroid.y, occludedUmbraAlpha); - for (int i = 0; i < realUmbraVertexCount; i++) { - indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[i]; + centroid.y, M_PI); + for (int i = 0; i < umbraLength; i++) { + indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = lastCentroidIndex; } - // Close the loop here! - indexBuffer[indexBufferIndex++] = slicedUmbraVertexIndex[0]; - indexBuffer[indexBufferIndex++] = lastCentroidIndex; - } - -#if DEBUG_SHADOW - ALOGD("allocated IB %d allocated VB is %d", totalIndexCount, totalVertexCount); - ALOGD("IB index %d VB index is %d", indexBufferIndex, vertexBufferIndex); - for (int i = 0; i < vertexBufferIndex; i++) { - ALOGD("vertexBuffer i %d, (%f, %f %f)", i, shadowVertices[i].x, shadowVertices[i].y, - shadowVertices[i].alpha); - } - for (int i = 0; i < indexBufferIndex; i++) { - ALOGD("indexBuffer i %d, indexBuffer[i] %d", i, indexBuffer[i]); } -#endif + // Closing the umbra area triangle's loop here. + indexBuffer[indexBufferIndex++] = newPenumbraLength; + indexBuffer[indexBufferIndex++] = savedStartIndex; // At the end, update the real index and vertex buffer size. shadowTriangleStrip.updateVertexCount(vertexBufferIndex); @@ -1585,8 +1088,8 @@ bool SpotShadow::testConvex(const Vector2* polygon, int polygonLength, Vector2 middle = polygon[(i + 1) % polygonLength]; Vector2 end = polygon[(i + 2) % polygonLength]; - double delta = (double(middle.x) - start.x) * (double(end.y) - start.y) - - (double(middle.y) - start.y) * (double(end.x) - start.x); + float delta = (float(middle.x) - start.x) * (float(end.y) - start.y) - + (float(middle.y) - start.y) * (float(end.x) - start.x); bool isCCWOrCoLinear = (delta >= EPSILON); if (isCCWOrCoLinear) { @@ -1621,8 +1124,8 @@ void SpotShadow::testIntersection(const Vector2* poly1, int poly1Length, bool dumpPoly = false; for (int k = 0; k < TEST_POINT_NUMBER; k++) { // Generate a random point between minX, minY and maxX, maxY. - double randomX = rand() / double(RAND_MAX); - double randomY = rand() / double(RAND_MAX); + float randomX = rand() / float(RAND_MAX); + float randomY = rand() / float(RAND_MAX); Vector2 testPoint; testPoint.x = lowerBound.x + randomX * (upperBound.x - lowerBound.x); diff --git a/libs/hwui/SpotShadow.h b/libs/hwui/SpotShadow.h index 23fdca9..e2d94f7 100644 --- a/libs/hwui/SpotShadow.h +++ b/libs/hwui/SpotShadow.h @@ -35,42 +35,6 @@ private: static float projectCasterToOutline(Vector2& outline, const Vector3& lightCenter, const Vector3& polyVertex); - static int calculateOccludedUmbra(const Vector2* umbra, int umbraLength, - const Vector3* poly, int polyLength, Vector2* occludedUmbra); - - static int setupAngleList(VertexAngleData* angleDataList, - int polyLength, const Vector2* polygon, const Vector2& centroid, - bool isPenumbra, const char* name); - - static int convertPolysToVerticesPerRay( - bool hasOccludedUmbraArea, const Vector2* poly2d, int polyLength, - const Vector2* umbra, int umbraLength, const Vector2* penumbra, - int penumbraLength, const Vector2& centroid, - Vector2* umbraVerticesPerRay, Vector2* penumbraVerticesPerRay, - Vector2* occludedUmbraVerticesPerRay); - - static bool checkClockwise(int maxIndex, int listLength, - VertexAngleData* angleList, const char* name); - - static void calculateDistanceCounter(bool needsOffsetToUmbra, int angleLength, - const VertexAngleData* allVerticesAngleData, int* distances); - - static void mergeAngleList(int maxUmbraAngleIndex, int maxPenumbraAngleIndex, - const VertexAngleData* umbraAngleList, int umbraLength, - const VertexAngleData* penumbraAngleList, int penumbraLength, - VertexAngleData* allVerticesAngleData); - - static int setupPolyAngleList(float* polyAngleList, int polyAngleLength, - const Vector2* poly2d, const Vector2& centroid); - - static bool checkPolyClockwise(int polyAngleLength, int maxPolyAngleIndex, - const float* polyAngleList); - - static int getEdgeStartIndex(const int* offsets, int rayIndex, int totalRayNumber, - const VertexAngleData* allVerticesAngleData); - - static int getPolyEdgeStartIndex(int maxPolyAngleIndex, int polyLength, - const float* polyAngleList, float rayAngle); static void computeLightPolygon(int points, const Vector3& lightCenter, float size, Vector3* ret); @@ -81,8 +45,7 @@ private: static void xsort(Vector2* points, int pointsLength); static int hull(Vector2* points, int pointsLength, Vector2* retPoly); - static bool ccw(double ax, double ay, double bx, double by, double cx, double cy); - static int intersection(const Vector2* poly1, int poly1length, Vector2* poly2, int poly2length); + static bool ccw(float ax, float ay, float bx, float by, float cx, float cy); static void sort(Vector2* poly, int polyLength, const Vector2& center); static void swap(Vector2* points, int i, int j); @@ -92,8 +55,6 @@ private: static bool testPointInsidePolygon(const Vector2 testPoint, const Vector2* poly, int len); static void makeClockwise(Vector2* polygon, int len); static void reverse(Vector2* polygon, int len); - static inline bool lineIntersection(double x1, double y1, double x2, double y2, - double x3, double y3, double x4, double y4, Vector2& ret); static void generateTriangleStrip(bool isCasterOpaque, float shadowStrengthScale, Vector2* penumbra, int penumbraLength, Vector2* umbra, int umbraLength, diff --git a/libs/hwui/StatefulBaseRenderer.h b/libs/hwui/StatefulBaseRenderer.h index 745e48a..fcd6060 100644 --- a/libs/hwui/StatefulBaseRenderer.h +++ b/libs/hwui/StatefulBaseRenderer.h @@ -129,7 +129,8 @@ protected: * * Subclasses can override this method to handle layer restoration */ - virtual void onSnapshotRestored(const Snapshot& removed, const Snapshot& restored) {}; + virtual void onSnapshotRestored(const Snapshot& removed, + const Snapshot& restored) {}; virtual void onViewportInitialized() {}; diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h index 688a699..7eca681 100644 --- a/libs/hwui/TessellationCache.h +++ b/libs/hwui/TessellationCache.h @@ -178,7 +178,8 @@ private: // holds a pointer, and implicit strong ref to each shadow task of the frame LruCache<ShadowDescription, Task<vertexBuffer_pair_t*>*> mShadowCache; class BufferPairRemovedListener : public OnEntryRemoved<ShadowDescription, Task<vertexBuffer_pair_t*>*> { - void operator()(ShadowDescription& description, Task<vertexBuffer_pair_t*>*& bufferPairTask) { + void operator()(ShadowDescription& description, + Task<vertexBuffer_pair_t*>*& bufferPairTask) { bufferPairTask->decStrong(NULL); } }; diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp index 3b8a9a4..63454d8 100644 --- a/libs/hwui/TextureCache.cpp +++ b/libs/hwui/TextureCache.cpp @@ -20,12 +20,15 @@ #include <GLES2/gl2.h> #include <SkCanvas.h> +#include <SkPixelRef.h> #include <utils/Mutex.h> +#include "AssetAtlas.h" #include "Caches.h" #include "TextureCache.h" #include "Properties.h" +#include "utils/TraceUtils.h" namespace android { namespace uirenderer { @@ -35,9 +38,9 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// TextureCache::TextureCache(): - mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity), + mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), mSize(0), mMaxSize(MB(DEFAULT_TEXTURE_CACHE_SIZE)), - mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE) { + mFlushRate(DEFAULT_TEXTURE_CACHE_FLUSH_RATE), mAssetAtlas(0) { char property[PROPERTY_VALUE_MAX]; if (property_get(PROPERTY_TEXTURE_CACHE_SIZE, property, NULL) > 0) { INIT_LOGD(" Setting texture cache size to %sMB", property); @@ -59,8 +62,8 @@ TextureCache::TextureCache(): } TextureCache::TextureCache(uint32_t maxByteSize): - mCache(LruCache<const SkPixelRef*, Texture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(maxByteSize) { + mCache(LruCache<uint32_t, Texture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(maxByteSize), mAssetAtlas(0) { init(); } @@ -104,7 +107,7 @@ void TextureCache::setFlushRate(float flushRate) { // Callbacks /////////////////////////////////////////////////////////////////////////////// -void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) { +void TextureCache::operator()(uint32_t&, Texture*& texture) { // This will be called already locked if (texture) { mSize -= texture->bitmapSize; @@ -122,8 +125,12 @@ void TextureCache::operator()(const SkPixelRef*&, Texture*& texture) { // Caching /////////////////////////////////////////////////////////////////////////////// +void TextureCache::setAssetAtlas(AssetAtlas* assetAtlas) { + mAssetAtlas = assetAtlas; +} + void TextureCache::resetMarkInUse() { - LruCache<const SkPixelRef*, Texture*>::Iterator iter(mCache); + LruCache<uint32_t, Texture*>::Iterator iter(mCache); while (iter.next()) { iter.value()->isInUse = false; } @@ -141,7 +148,14 @@ bool TextureCache::canMakeTextureFromBitmap(const SkBitmap* bitmap) { // Returns a prepared Texture* that either is already in the cache or can fit // in the cache (and is thus added to the cache) Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { - Texture* texture = mCache.get(bitmap->pixelRef()); + if (CC_LIKELY(mAssetAtlas)) { + AssetAtlas::Entry* entry = mAssetAtlas->getEntry(bitmap); + if (CC_UNLIKELY(entry)) { + return entry->texture; + } + } + + Texture* texture = mCache.get(bitmap->pixelRef()->getStableID()); if (!texture) { if (!canMakeTextureFromBitmap(bitmap)) { @@ -171,7 +185,7 @@ Texture* TextureCache::getCachedTexture(const SkBitmap* bitmap) { if (mDebugEnabled) { ALOGD("Texture created, size = %d", size); } - mCache.put(bitmap->pixelRef(), texture); + mCache.put(bitmap->pixelRef()->getStableID(), texture); } } else if (!texture->isInUse && bitmap->getGenerationID() != texture->generation) { // Texture was in the cache but is dirty, re-upload @@ -218,22 +232,19 @@ Texture* TextureCache::getTransient(const SkBitmap* bitmap) { return texture; } -void TextureCache::remove(const SkBitmap* bitmap) { - mCache.remove(bitmap->pixelRef()); -} +void TextureCache::releaseTexture(const SkBitmap* bitmap) { + if (!bitmap || !bitmap->pixelRef()) return; -void TextureCache::removeDeferred(const SkBitmap* bitmap) { Mutex::Autolock _l(mLock); - mGarbage.push(bitmap); + mGarbage.push(bitmap->pixelRef()->getStableID()); } void TextureCache::clearGarbage() { Mutex::Autolock _l(mLock); size_t count = mGarbage.size(); for (size_t i = 0; i < count; i++) { - const SkBitmap* bitmap = mGarbage.itemAt(i); - mCache.remove(bitmap->pixelRef()); - delete bitmap; + uint32_t pixelRefId = mGarbage.itemAt(i); + mCache.remove(pixelRefId); } mGarbage.clear(); } @@ -266,7 +277,7 @@ void TextureCache::generateTexture(const SkBitmap* bitmap, Texture* texture, boo return; } - ATRACE_CALL(); + ATRACE_FORMAT("Upload %ux%u Texture", bitmap->width(), bitmap->height()); // We could also enable mipmapping if both bitmap dimensions are powers // of 2 but we'd have to deal with size changes. Let's keep this simple diff --git a/libs/hwui/TextureCache.h b/libs/hwui/TextureCache.h index 61db5b0..cf8d134 100644 --- a/libs/hwui/TextureCache.h +++ b/libs/hwui/TextureCache.h @@ -44,12 +44,14 @@ namespace uirenderer { // Classes /////////////////////////////////////////////////////////////////////////////// +class AssetAtlas; + /** * A simple LRU texture cache. The cache has a maximum size expressed in bytes. * Any texture added to the cache causing the cache to grow beyond the maximum * allowed size will also cause the oldest texture to be kicked out. */ -class TextureCache: public OnEntryRemoved<const SkPixelRef*, Texture*> { +class TextureCache: public OnEntryRemoved<uint32_t, Texture*> { public: TextureCache(); TextureCache(uint32_t maxByteSize); @@ -59,7 +61,7 @@ public: * Used as a callback when an entry is removed from the cache. * Do not invoke directly. */ - void operator()(const SkPixelRef*& pixelRef, Texture*& texture); + void operator()(uint32_t&, Texture*& texture); /** * Resets all Textures to not be marked as in use @@ -83,16 +85,12 @@ public: * texture is not kept in the cache. The caller must destroy the texture. */ Texture* getTransient(const SkBitmap* bitmap); - /** - * Removes the texture associated with the specified bitmap. - * Upon remove the texture is freed. - */ - void remove(const SkBitmap* bitmap); + /** * Removes the texture associated with the specified bitmap. This is meant * to be called from threads that are not the EGL context thread. */ - void removeDeferred(const SkBitmap* bitmap); + void releaseTexture(const SkBitmap* bitmap); /** * Process deferred removals. */ @@ -127,6 +125,8 @@ public: */ void setFlushRate(float flushRate); + void setAssetAtlas(AssetAtlas* assetAtlas); + private: bool canMakeTextureFromBitmap(const SkBitmap* bitmap); @@ -147,7 +147,7 @@ private: void init(); - LruCache<const SkPixelRef*, Texture*> mCache; + LruCache<uint32_t, Texture*> mCache; uint32_t mSize; uint32_t mMaxSize; @@ -157,8 +157,10 @@ private: bool mDebugEnabled; - Vector<const SkBitmap*> mGarbage; + Vector<uint32_t> mGarbage; mutable Mutex mLock; + + AssetAtlas* mAssetAtlas; }; // class TextureCache }; // namespace uirenderer diff --git a/libs/hwui/Vector.h b/libs/hwui/Vector.h index d033ed9..aa6acc9 100644 --- a/libs/hwui/Vector.h +++ b/libs/hwui/Vector.h @@ -99,6 +99,10 @@ struct Vector2 { return x * v.x + y * v.y; } + float cross(const Vector2& v) const { + return x * v.y - y * v.x; + } + void dump() { ALOGD("Vector2[%.2f, %.2f]", x, y); } diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index bf55558..e1a38dd 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -139,7 +139,8 @@ void Font::invalidateTextureCache(CacheTexture* cacheTexture) { } void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* pos) { int width = (int) glyph->mBitmapWidth; int height = (int) glyph->mBitmapHeight; @@ -161,7 +162,8 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, } void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* pos) { float width = (float) glyph->mBitmapWidth; float height = (float) glyph->mBitmapHeight; @@ -180,7 +182,8 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y, } void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, - uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) { + uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, + const float* pos) { float width = (float) glyph->mBitmapWidth; float height = (float) glyph->mBitmapHeight; @@ -210,7 +213,8 @@ void Font::drawCachedGlyphTransformed(CachedGlyphInfo* glyph, int x, int y, } void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, uint8_t* bitmap, - uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, const float* pos) { + uint32_t bitmapWidth, uint32_t bitmapHeight, Rect* bounds, + const float* pos) { int dstX = x + glyph->mBitmapLeft; int dstY = y + glyph->mBitmapTop; @@ -363,7 +367,7 @@ void Font::measure(const SkPaint* paint, const char* text, uint32_t start, uint3 } void Font::precache(const SkPaint* paint, const char* text, int numGlyphs) { - ATRACE_NAME("precacheText"); + ATRACE_NAME("Precache Glyphs"); if (numGlyphs == 0 || text == NULL) { return; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 92a30e5..b7e1752 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -42,7 +42,8 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, : mRenderThread(thread) , mEglManager(thread.eglManager()) , mEglSurface(EGL_NO_SURFACE) - , mDirtyRegionsEnabled(false) + , mBufferPreserved(false) + , mSwapBehavior(kSwap_default) , mOpaque(!translucent) , mCanvas(NULL) , mHaveNewSurface(false) @@ -70,6 +71,8 @@ void CanvasContext::destroy() { } void CanvasContext::setSurface(ANativeWindow* window) { + ATRACE_CALL(); + mNativeWindow = window; if (mEglSurface != EGL_NO_SURFACE) { @@ -82,7 +85,8 @@ void CanvasContext::setSurface(ANativeWindow* window) { } if (mEglSurface != EGL_NO_SURFACE) { - mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface); + const bool preserveBuffer = (mSwapBehavior != kSwap_discardBuffer); + mBufferPreserved = mEglManager.setPreserveBuffer(mEglSurface, preserveBuffer); mHaveNewSurface = true; makeCurrent(); } else { @@ -103,6 +107,10 @@ void CanvasContext::requireSurface() { makeCurrent(); } +void CanvasContext::setSwapBehavior(SwapBehavior swapBehavior) { + mSwapBehavior = swapBehavior; +} + bool CanvasContext::initialize(ANativeWindow* window) { setSurface(window); if (mCanvas) return false; @@ -115,13 +123,13 @@ void CanvasContext::updateSurface(ANativeWindow* window) { setSurface(window); } -void CanvasContext::pauseSurface(ANativeWindow* window) { - stopDrawing(); +bool CanvasContext::pauseSurface(ANativeWindow* window) { + return mRenderThread.removeFrameCallback(this); } // TODO: don't pass viewport size, it's automatic via EGL -void CanvasContext::setup(int width, int height, const Vector3& lightCenter, float lightRadius, - uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { +void CanvasContext::setup(int width, int height, const Vector3& lightCenter, + float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha) { if (!mCanvas) return; mCanvas->initLight(lightCenter, lightRadius, ambientShadowAlpha, spotShadowAlpha); } @@ -160,6 +168,11 @@ void CanvasContext::prepareTree(TreeInfo& info) { freePrefetechedLayers(); } + if (CC_UNLIKELY(!mNativeWindow.get())) { + info.out.canDrawThisFrame = false; + return; + } + int runningBehind = 0; // TODO: This query is moderately expensive, investigate adding some sort // of fast-path based off when we last called eglSwapBuffers() as well as @@ -200,7 +213,7 @@ void CanvasContext::draw() { if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) { mCanvas->setViewport(width, height); dirty.setEmpty(); - } else if (!mDirtyRegionsEnabled || mHaveNewSurface) { + } else if (!mBufferPreserved || mHaveNewSurface) { dirty.setEmpty(); } else { if (!dirty.isEmpty() && !dirty.intersect(0, 0, width, height)) { @@ -230,6 +243,8 @@ void CanvasContext::draw() { if (status & DrawGlInfo::kStatusDrew) { swapBuffers(); + } else { + mEglManager.cancelFrame(); } profiler().finishFrame(); @@ -330,6 +345,7 @@ void CanvasContext::trimMemory(RenderThread& thread, int level) { // No context means nothing to free if (!thread.eglManager().hasEglContext()) return; + ATRACE_CALL(); thread.eglManager().requireGlContext(); if (level >= TRIM_MEMORY_COMPLETE) { Caches::getInstance().flush(Caches::kFlushMode_Full); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index d4282fa..0cc2c7c 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -48,6 +48,11 @@ namespace renderthread { class EglManager; +enum SwapBehavior { + kSwap_default, + kSwap_discardBuffer, +}; + // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager @@ -57,9 +62,14 @@ public: IContextFactory* contextFactory); virtual ~CanvasContext(); + // Won't take effect until next EGLSurface creation + void setSwapBehavior(SwapBehavior swapBehavior); + bool initialize(ANativeWindow* window); void updateSurface(ANativeWindow* window); - void pauseSurface(ANativeWindow* window); + bool pauseSurface(ANativeWindow* window); + bool hasSurface() { return mNativeWindow.get(); } + void setup(int width, int height, const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setOpaque(bool opaque); @@ -111,7 +121,8 @@ private: EglManager& mEglManager; sp<ANativeWindow> mNativeWindow; EGLSurface mEglSurface; - bool mDirtyRegionsEnabled; + bool mBufferPreserved; + SwapBehavior mSwapBehavior; bool mOpaque; OpenGLRenderer* mCanvas; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index dd34e09..97b31a9 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -132,6 +132,12 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { mLayers.clear(); mContext->prepareTree(info); + // This is after the prepareTree so that any pending operations + // (RenderNode tree state, prefetched layers, etc...) will be flushed. + if (CC_UNLIKELY(!mContext->hasSurface())) { + mSyncResult |= kSync_LostSurfaceRewardIfFound; + } + if (info.out.hasAnimations) { if (info.out.requiresUiRedraw) { mSyncResult |= kSync_UIRedrawRequired; diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index 243cc5d..28f6cb2 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -42,6 +42,7 @@ class RenderThread; enum SyncResult { kSync_OK = 0, kSync_UIRedrawRequired = 1 << 0, + kSync_LostSurfaceRewardIfFound = 1 << 1, }; /* diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index a87834e..8fb1b10 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -70,17 +70,20 @@ EglManager::EglManager(RenderThread& thread) , mEglConfig(0) , mEglContext(EGL_NO_CONTEXT) , mPBufferSurface(EGL_NO_SURFACE) - , mRequestDirtyRegions(load_dirty_regions_property()) + , mAllowPreserveBuffer(load_dirty_regions_property()) , mCurrentSurface(EGL_NO_SURFACE) , mAtlasMap(NULL) - , mAtlasMapSize(0) { - mCanSetDirtyRegions = mRequestDirtyRegions; - ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false"); + , mAtlasMapSize(0) + , mInFrame(false) { + mCanSetPreserveBuffer = mAllowPreserveBuffer; + ALOGD("Use EGL_SWAP_BEHAVIOR_PRESERVED: %s", mAllowPreserveBuffer ? "true" : "false"); } void EglManager::initialize() { if (hasEglContext()) return; + ATRACE_NAME("Creating EGLContext"); + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str()); @@ -105,15 +108,16 @@ bool EglManager::hasEglContext() { 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) { + if (!mInFrame) { + // We can't be certain about the state of the current surface (whether + // or not it is destroyed, for example), so err on the side of using + // the pbuffer surface which we fully control usePBufferSurface(); } } void EglManager::loadConfig() { - EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; + EGLint swapBehavior = mCanSetPreserveBuffer ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0; EGLint attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_RED_SIZE, 8, @@ -131,10 +135,10 @@ void EglManager::loadConfig() { if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs) || num_configs != 1) { // Failed to get a valid config - if (mCanSetDirtyRegions) { + if (mCanSetPreserveBuffer) { ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without..."); // Try again without dirty regions enabled - mCanSetDirtyRegions = false; + mCanSetPreserveBuffer = false; loadConfig(); } else { LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str()); @@ -171,7 +175,8 @@ void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer, void EglManager::initAtlas() { if (mAtlasBuffer.get()) { - Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize); + mRenderThread.renderState().assetAtlas().init(mAtlasBuffer, + mAtlasMap, mAtlasMapSize); } } @@ -252,9 +257,11 @@ void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) { eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height); } eglBeginFrame(mEglDisplay, surface); + mInFrame = true; } bool EglManager::swapBuffers(EGLSurface surface) { + mInFrame = false; eglSwapBuffers(mEglDisplay, surface); EGLint err = eglGetError(); if (CC_LIKELY(err == EGL_SUCCESS)) { @@ -273,25 +280,34 @@ bool EglManager::swapBuffers(EGLSurface surface) { return false; } -bool EglManager::enableDirtyRegions(EGLSurface surface) { - if (!mRequestDirtyRegions) return false; +void EglManager::cancelFrame() { + mInFrame = false; +} + +bool EglManager::setPreserveBuffer(EGLSurface surface, bool preserve) { + if (CC_UNLIKELY(!mAllowPreserveBuffer)) return false; - if (mCanSetDirtyRegions) { - if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) { + bool preserved = false; + if (mCanSetPreserveBuffer) { + preserved = eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, + preserve ? EGL_BUFFER_PRESERVED : EGL_BUFFER_DESTROYED); + if (CC_UNLIKELY(!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; + if (CC_UNLIKELY(!preserved)) { + // Maybe it's already set? + EGLint swapBehavior; + if (eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &swapBehavior)) { + preserved = (swapBehavior == EGL_BUFFER_PRESERVED); + } else { + ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p", + (void*) surface, egl_error_str()); + } } - return value == EGL_BUFFER_PRESERVED; + + return preserved; } } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h index 71213fb..e12db3a 100644 --- a/libs/hwui/renderthread/EglManager.h +++ b/libs/hwui/renderthread/EglManager.h @@ -48,8 +48,10 @@ public: bool makeCurrent(EGLSurface surface); void beginFrame(EGLSurface surface, EGLint* width, EGLint* height); bool swapBuffers(EGLSurface surface); + void cancelFrame(); - bool enableDirtyRegions(EGLSurface surface); + // Returns true iff the surface is now preserving buffers. + bool setPreserveBuffer(EGLSurface surface, bool preserve); void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize); @@ -71,14 +73,20 @@ private: EGLContext mEglContext; EGLSurface mPBufferSurface; - const bool mRequestDirtyRegions; - bool mCanSetDirtyRegions; + const bool mAllowPreserveBuffer; + bool mCanSetPreserveBuffer; EGLSurface mCurrentSurface; sp<GraphicBuffer> mAtlasBuffer; int64_t* mAtlasMap; size_t mAtlasMapSize; + + // Whether or not we are in the middle of drawing a frame. This is used + // to avoid switching surfaces mid-frame if requireGlContext() is called + // TODO: Need to be better about surface/context management so that this isn't + // necessary + bool mInFrame; }; } /* namespace renderthread */ diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index c6ea82b..36ba3a9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -103,6 +103,18 @@ void RenderProxy::setFrameInterval(nsecs_t frameIntervalNanos) { post(task); } +CREATE_BRIDGE2(setSwapBehavior, CanvasContext* context, SwapBehavior swapBehavior) { + args->context->setSwapBehavior(args->swapBehavior); + return NULL; +} + +void RenderProxy::setSwapBehavior(SwapBehavior swapBehavior) { + SETUP_TASK(setSwapBehavior); + args->context = mContext; + args->swapBehavior = swapBehavior; + post(task); +} + CREATE_BRIDGE1(loadSystemProperties, CanvasContext* context) { bool needsRedraw = false; if (Caches::hasInstance()) { @@ -144,15 +156,14 @@ void RenderProxy::updateSurface(const sp<ANativeWindow>& window) { } CREATE_BRIDGE2(pauseSurface, CanvasContext* context, ANativeWindow* window) { - args->context->pauseSurface(args->window); - return NULL; + return (void*) args->context->pauseSurface(args->window); } -void RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { +bool RenderProxy::pauseSurface(const sp<ANativeWindow>& window) { SETUP_TASK(pauseSurface); args->context = mContext; args->window = window.get(); - postAndWait(task); + return (bool) postAndWait(task); } CREATE_BRIDGE7(setup, CanvasContext* context, int width, int height, @@ -223,12 +234,7 @@ void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) { // 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); + staticPostAndWait(task); } else { thread.queue(task); } @@ -246,17 +252,6 @@ void RenderProxy::runWithGlContext(RenderTask* gltask) { postAndWait(task); } -CREATE_BRIDGE1(destroyLayer, Layer* layer) { - LayerRenderer::destroyLayer(args->layer); - return NULL; -} - -void RenderProxy::enqueueDestroyLayer(Layer* layer) { - SETUP_TASK(destroyLayer); - args->layer = layer; - RenderThread::getInstance().queue(task); -} - CREATE_BRIDGE2(createTextureLayer, RenderThread* thread, CanvasContext* context) { Layer* layer = args->context->createTextureLayer(); if (!layer) return 0; @@ -344,14 +339,16 @@ void RenderProxy::trimMemory(int level) { } } +template <typename T> +void UNUSED(T) {} + + CREATE_BRIDGE0(fence) { // Intentionally empty + UNUSED(args); return NULL; } -template <typename T> -void UNUSED(T t) {} - void RenderProxy::fence() { SETUP_TASK(fence); UNUSED(args); @@ -392,6 +389,17 @@ void RenderProxy::dumpProfileInfo(int fd) { postAndWait(task); } +CREATE_BRIDGE1(outputLogBuffer, int fd) { + RenderNode::outputLogBuffer(args->fd); + return NULL; +} + +void RenderProxy::outputLogBuffer(int fd) { + SETUP_TASK(outputLogBuffer); + args->fd = fd; + staticPostAndWait(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); @@ -422,6 +430,19 @@ void* RenderProxy::postAndWait(MethodInvokeRenderTask* task) { return retval; } +void* RenderProxy::staticPostAndWait(MethodInvokeRenderTask* task) { + RenderThread& thread = RenderThread::getInstance(); + void* retval; + task->setReturnPtr(&retval); + Mutex mutex; + Condition condition; + SignalingRenderTask syncTask(task, &mutex, &condition); + AutoMutex _lock(mutex); + thread.queue(&syncTask); + condition.wait(mutex); + return retval; +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 678e7e2..fd1fe05 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -31,6 +31,7 @@ #include "../Caches.h" #include "../IContextFactory.h" +#include "CanvasContext.h" #include "DrawFrameTask.h" namespace android { @@ -44,7 +45,6 @@ class Rect; namespace renderthread { -class CanvasContext; class ErrorChannel; class RenderThread; class RenderProxyBridge; @@ -63,11 +63,13 @@ public: ANDROID_API virtual ~RenderProxy(); ANDROID_API void setFrameInterval(nsecs_t frameIntervalNanos); + // Won't take effect until next EGLSurface creation + ANDROID_API void setSwapBehavior(SwapBehavior swapBehavior); ANDROID_API bool loadSystemProperties(); ANDROID_API bool initialize(const sp<ANativeWindow>& window); ANDROID_API void updateSurface(const sp<ANativeWindow>& window); - ANDROID_API void pauseSurface(const sp<ANativeWindow>& window); + ANDROID_API bool pauseSurface(const sp<ANativeWindow>& window); ANDROID_API void setup(int width, int height, const Vector3& lightCenter, float lightRadius, uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); ANDROID_API void setOpaque(bool opaque); @@ -79,7 +81,6 @@ public: ANDROID_API void runWithGlContext(RenderTask* task); - static void enqueueDestroyLayer(Layer* layer); ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API void buildLayer(RenderNode* node); ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); @@ -95,6 +96,7 @@ public: ANDROID_API void notifyFramePending(); ANDROID_API void dumpProfileInfo(int fd); + ANDROID_API static void outputLogBuffer(int fd); ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size); @@ -112,6 +114,8 @@ private: void post(RenderTask* task); void* postAndWait(MethodInvokeRenderTask* task); + static void* staticPostAndWait(MethodInvokeRenderTask* task); + // Friend class to help with bridging friend class RenderProxyBridge; }; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 403e164..38cb4cd 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -168,7 +168,7 @@ void RenderThread::initializeDisplayEventReceiver() { void RenderThread::initThreadLocals() { initializeDisplayEventReceiver(); mEglManager = new EglManager(*this); - mRenderState = new RenderState(); + mRenderState = new RenderState(*this); } int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) { @@ -314,9 +314,11 @@ void RenderThread::postFrameCallback(IFrameCallback* callback) { mPendingRegistrationFrameCallbacks.insert(callback); } -void RenderThread::removeFrameCallback(IFrameCallback* callback) { - mFrameCallbacks.erase(callback); - mPendingRegistrationFrameCallbacks.erase(callback); +bool RenderThread::removeFrameCallback(IFrameCallback* callback) { + size_t erased; + erased = mFrameCallbacks.erase(callback); + erased |= mPendingRegistrationFrameCallbacks.erase(callback); + return erased; } void RenderThread::pushBackFrameCallback(IFrameCallback* callback) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index c461f3a..4126d02 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -80,7 +80,7 @@ public: // Mimics android.view.Choreographer void postFrameCallback(IFrameCallback* callback); - void removeFrameCallback(IFrameCallback* callback); + bool removeFrameCallback(IFrameCallback* callback); // If the callback is currently registered, it will be pushed back until // the next vsync. If it is not currently registered this does nothing. void pushBackFrameCallback(IFrameCallback* callback); diff --git a/libs/hwui/renderthread/TimeLord.cpp b/libs/hwui/renderthread/TimeLord.cpp index cf3d039..9bd4eae 100644 --- a/libs/hwui/renderthread/TimeLord.cpp +++ b/libs/hwui/renderthread/TimeLord.cpp @@ -20,7 +20,7 @@ namespace uirenderer { namespace renderthread { TimeLord::TimeLord() - : mFrameIntervalNanos(0) + : mFrameIntervalNanos(milliseconds_to_nanoseconds(16)) , mFrameTimeNanos(0) { } diff --git a/libs/hwui/tests/Android.mk b/libs/hwui/tests/Android.mk new file mode 100644 index 0000000..7bdce7f --- /dev/null +++ b/libs/hwui/tests/Android.mk @@ -0,0 +1,56 @@ +# +# 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. +# + +local_target_dir := $(TARGET_OUT_DATA)/local/tmp +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES +LOCAL_CFLAGS += -Wno-unused-parameter +LOCAL_CFLAGS += -DATRACE_TAG=ATRACE_TAG_VIEW -DLOG_TAG=\"OpenGLRenderer\" + +LOCAL_SRC_FILES:= \ + TestContext.cpp \ + main.cpp + +LOCAL_C_INCLUDES += \ + $(LOCAL_PATH)/.. \ + external/skia/src/core + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libutils \ + libskia \ + libgui \ + libui \ + libhwui + +ifeq ($(WITH_MALLOC_LEAK_CHECK),true) + LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK +endif + +LOCAL_MODULE_PATH := $(local_target_dir) +LOCAL_MODULE:= hwuitest +LOCAL_MODULE_TAGS := tests +LOCAL_MULTILIB := both +LOCAL_MODULE_STEM_32 := hwuitest +LOCAL_MODULE_STEM_64 := hwuitest64 + +include external/stlport/libstlport.mk +include $(BUILD_EXECUTABLE) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/libs/hwui/tests/TestContext.cpp b/libs/hwui/tests/TestContext.cpp new file mode 100644 index 0000000..35e402d --- /dev/null +++ b/libs/hwui/tests/TestContext.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestContext.h" + +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> + +using namespace android; + +DisplayInfo gDisplay; +sp<SurfaceComposerClient> gSession; + +void createTestEnvironment() { + gSession = new SurfaceComposerClient(); + sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay( + ISurfaceComposer::eDisplayIdMain)); + status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &gDisplay); + LOG_ALWAYS_FATAL_IF(status, "Failed to get display info\n"); +} + +sp<SurfaceControl> createWindow(int width, int height) { + sp<SurfaceControl> control = gSession->createSurface(String8("HwuiTest"), + width, height, PIXEL_FORMAT_RGBX_8888); + + SurfaceComposerClient::openGlobalTransaction(); + control->setLayer(0x7FFFFFF); + control->show(); + SurfaceComposerClient::closeGlobalTransaction(); + + return control; +} diff --git a/libs/hwui/tests/TestContext.h b/libs/hwui/tests/TestContext.h new file mode 100644 index 0000000..8a5d530 --- /dev/null +++ b/libs/hwui/tests/TestContext.h @@ -0,0 +1,33 @@ +/* + * 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 TESTCONTEXT_H +#define TESTCONTEXT_H + +#include <ui/DisplayInfo.h> +#include <gui/SurfaceControl.h> + +extern android::DisplayInfo gDisplay; +#define dp(x) ((x) * gDisplay.density) + +// Initializes all the static globals that are shared across all contexts +// such as display info +void createTestEnvironment(); + +// Defaults to fullscreen +android::sp<android::SurfaceControl> createWindow(int width = -1, int height = -1); + +#endif diff --git a/libs/hwui/tests/main.cpp b/libs/hwui/tests/main.cpp new file mode 100644 index 0000000..d847d13 --- /dev/null +++ b/libs/hwui/tests/main.cpp @@ -0,0 +1,131 @@ +/* + * 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 <stdio.h> + +#include <cutils/log.h> +#include <gui/Surface.h> +#include <ui/PixelFormat.h> + +#include <AnimationContext.h> +#include <DisplayListRenderer.h> +#include <RenderNode.h> +#include <renderthread/RenderProxy.h> + +#include "TestContext.h" + +using namespace android; +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +class ContextFactory : public IContextFactory { +public: + virtual AnimationContext* createAnimationContext(renderthread::TimeLord& clock) { + return new AnimationContext(clock); + } +}; + +static DisplayListRenderer* startRecording(RenderNode* node) { + DisplayListRenderer* renderer = new DisplayListRenderer(); + renderer->setViewport(node->getWidth(), node->getHeight()); + renderer->prepare(false); + return renderer; +} + +static void endRecording(DisplayListRenderer* renderer, RenderNode* node) { + renderer->finish(); + node->setStagingDisplayList(renderer->finishRecording()); + delete renderer; +} + +sp<RenderNode> createCard(int x, int y, int width, int height) { + sp<RenderNode> node = new RenderNode(); + node->mutateStagingProperties().setLeftTopRightBottom(x, y, x + width, y + height); + node->mutateStagingProperties().setElevation(dp(16)); + node->mutateStagingProperties().mutableOutline().setRoundRect(0, 0, width, height, dp(10), 1); + node->mutateStagingProperties().mutableOutline().setShouldClip(true); + node->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y | RenderNode::Z); + + DisplayListRenderer* renderer = startRecording(node.get()); + renderer->drawColor(0xFFEEEEEE, SkXfermode::kSrcOver_Mode); + endRecording(renderer, node.get()); + + return node; +} + +int main() { + createTestEnvironment(); + + // create the native surface + const int width = gDisplay.w; + const int height = gDisplay.h; + sp<SurfaceControl> control = createWindow(width, height); + sp<Surface> surface = control->getSurface(); + + RenderNode* rootNode = new RenderNode(); + rootNode->incStrong(0); + rootNode->mutateStagingProperties().setLeftTopRightBottom(0, 0, width, height); + rootNode->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + rootNode->mutateStagingProperties().setClipToBounds(false); + rootNode->setPropertyFieldsDirty(RenderNode::GENERIC); + + ContextFactory factory; + RenderProxy* proxy = new RenderProxy(false, rootNode, &factory); + proxy->loadSystemProperties(); + proxy->initialize(surface); + float lightX = width / 2.0; + proxy->setup(width, height, (Vector3){lightX, dp(-200.0f), dp(800.0f)}, + dp(800.0f), 255 * 0.075, 255 * 0.15); + + android::uirenderer::Rect DUMMY; + + std::vector< sp<RenderNode> > cards; + + DisplayListRenderer* renderer = startRecording(rootNode); + renderer->drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + renderer->insertReorderBarrier(true); + + for (int x = dp(16); x < (width - dp(116)); x += dp(116)) { + for (int y = dp(16); y < (height - dp(116)); y += dp(116)) { + sp<RenderNode> card = createCard(x, y, dp(100), dp(100)); + renderer->drawRenderNode(card.get(), DUMMY, 0); + cards.push_back(card); + } + } + + renderer->insertReorderBarrier(false); + endRecording(renderer, rootNode); + + for (int i = 0; i < 150; i++) { + ATRACE_NAME("UI-Draw Frame"); + for (size_t ci = 0; ci < cards.size(); ci++) { + cards[ci]->mutateStagingProperties().setTranslationX(i); + cards[ci]->mutateStagingProperties().setTranslationY(i); + cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + nsecs_t frameTimeNs = systemTime(CLOCK_MONOTONIC); + proxy->syncAndDrawFrame(frameTimeNs, 0, gDisplay.density); + usleep(12000); + } + + sleep(5); + + delete proxy; + rootNode->decStrong(0); + + printf("Success!\n"); + return 0; +} diff --git a/libs/hwui/utils/TraceUtils.h b/libs/hwui/utils/TraceUtils.h new file mode 100644 index 0000000..ff8ccb8 --- /dev/null +++ b/libs/hwui/utils/TraceUtils.h @@ -0,0 +1,56 @@ +/* + * 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 TRACE_UTILS_H +#define TRACE_UTILS_H + +#include <utils/Trace.h> + +#define ATRACE_FORMAT(fmt, ...) \ + TraceUtils::TraceEnder __traceEnder = (TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__), TraceUtils::TraceEnder()) + +#define ATRACE_FORMAT_BEGIN(fmt, ...) \ + TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__) + +namespace android { +namespace uirenderer { + +class TraceUtils { +public: + class TraceEnder { + public: + ~TraceEnder() { ATRACE_END(); } + }; + + static void atraceFormatBegin(const char* fmt, ...) { + if (CC_UNLIKELY(!ATRACE_ENABLED())) return; + + const int BUFFER_SIZE = 256; + va_list ap; + char buf[BUFFER_SIZE]; + + va_start(ap, fmt); + vsnprintf(buf, BUFFER_SIZE, fmt, ap); + va_end(ap); + + ATRACE_BEGIN(buf); + } + +}; // class TraceUtils + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* TRACE_UTILS_H */ |