diff options
Diffstat (limited to 'libs')
27 files changed, 557 insertions, 68 deletions
diff --git a/libs/androidfw/Asset.cpp b/libs/androidfw/Asset.cpp index 4e14b13dc8bd..6ae39d5d5d01 100644 --- a/libs/androidfw/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -52,6 +52,47 @@ static int32_t gCount = 0; static Asset* gHead = NULL; static Asset* gTail = NULL; +void Asset::registerAsset(Asset* asset) +{ + AutoMutex _l(gAssetLock); + gCount++; + asset->mNext = asset->mPrev = NULL; + if (gTail == NULL) { + gHead = gTail = asset; + } else { + asset->mPrev = gTail; + gTail->mNext = asset; + gTail = asset; + } + + if (kIsDebug) { + ALOGI("Creating Asset %p #%d\n", asset, gCount); + } +} + +void Asset::unregisterAsset(Asset* asset) +{ + AutoMutex _l(gAssetLock); + gCount--; + if (gHead == asset) { + gHead = asset->mNext; + } + if (gTail == asset) { + gTail = asset->mPrev; + } + if (asset->mNext != NULL) { + asset->mNext->mPrev = asset->mPrev; + } + if (asset->mPrev != NULL) { + asset->mPrev->mNext = asset->mNext; + } + asset->mNext = asset->mPrev = NULL; + + if (kIsDebug) { + ALOGI("Destroying Asset in %p #%d\n", asset, gCount); + } +} + int32_t Asset::getGlobalCount() { AutoMutex _l(gAssetLock); @@ -79,43 +120,8 @@ String8 Asset::getAssetAllocations() } Asset::Asset(void) - : mAccessMode(ACCESS_UNKNOWN) + : mAccessMode(ACCESS_UNKNOWN), mNext(NULL), mPrev(NULL) { - AutoMutex _l(gAssetLock); - gCount++; - mNext = mPrev = NULL; - if (gTail == NULL) { - gHead = gTail = this; - } else { - mPrev = gTail; - gTail->mNext = this; - gTail = this; - } - if (kIsDebug) { - ALOGI("Creating Asset %p #%d\n", this, gCount); - } -} - -Asset::~Asset(void) -{ - AutoMutex _l(gAssetLock); - gCount--; - if (gHead == this) { - gHead = mNext; - } - if (gTail == this) { - gTail = mPrev; - } - if (mNext != NULL) { - mNext->mPrev = mPrev; - } - if (mPrev != NULL) { - mPrev->mNext = mNext; - } - mNext = mPrev = NULL; - if (kIsDebug) { - ALOGI("Destroying Asset in %p #%d\n", this, gCount); - } } /* @@ -361,6 +367,9 @@ off64_t Asset::handleSeek(off64_t offset, int whence, off64_t curPosn, off64_t m _FileAsset::_FileAsset(void) : mStart(0), mLength(0), mOffset(0), mFp(NULL), mFileName(NULL), mMap(NULL), mBuf(NULL) { + // Register the Asset with the global list here after it is fully constructed and its + // vtable pointer points to this concrete type. b/31113965 + registerAsset(this); } /* @@ -369,6 +378,10 @@ _FileAsset::_FileAsset(void) _FileAsset::~_FileAsset(void) { close(); + + // Unregister the Asset from the global list here before it is destructed and while its vtable + // pointer still points to this concrete type. b/31113965 + unregisterAsset(this); } /* @@ -685,6 +698,9 @@ _CompressedAsset::_CompressedAsset(void) : mStart(0), mCompressedLen(0), mUncompressedLen(0), mOffset(0), mMap(NULL), mFd(-1), mZipInflater(NULL), mBuf(NULL) { + // Register the Asset with the global list here after it is fully constructed and its + // vtable pointer points to this concrete type. b/31113965 + registerAsset(this); } /* @@ -693,6 +709,10 @@ _CompressedAsset::_CompressedAsset(void) _CompressedAsset::~_CompressedAsset(void) { close(); + + // Unregister the Asset from the global list here before it is destructed and while its vtable + // pointer still points to this concrete type. b/31113965 + unregisterAsset(this); } /* diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 1bc4c244dac9..7a103430feb5 100644 --- a/libs/androidfw/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -35,6 +35,9 @@ #include <utils/threads.h> #include <utils/Timers.h> #include <utils/Trace.h> +#ifndef _WIN32 +#include <sys/file.h> +#endif #include <assert.h> #include <dirent.h> @@ -767,6 +770,12 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, return; } +#ifndef _WIN32 + if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) { + fclose(fin); + return; + } +#endif char buf[1024]; while (fgets(buf, sizeof(buf), fin)) { // format of each line: @@ -798,6 +807,10 @@ void AssetManager::addSystemOverlays(const char* pathOverlaysList, delete oidmap; } } + +#ifndef _WIN32 + TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN)); +#endif fclose(fin); } @@ -1893,6 +1906,7 @@ ZipFileRO* AssetManager::SharedZip::getZip() Asset* AssetManager::SharedZip::getResourceTableAsset() { + AutoMutex _l(gLock); ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset); return mResourceTableAsset; } @@ -1902,10 +1916,10 @@ Asset* AssetManager::SharedZip::setResourceTableAsset(Asset* asset) { AutoMutex _l(gLock); if (mResourceTableAsset == NULL) { - mResourceTableAsset = asset; // This is not thread safe the first time it is called, so // do it here with the global lock held. asset->getBuffer(true); + mResourceTableAsset = asset; return asset; } } diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk index 2bc026b79ca2..1fe1773578fa 100644 --- a/libs/androidfw/tests/Android.mk +++ b/libs/androidfw/tests/Android.mk @@ -22,6 +22,7 @@ LOCAL_PATH:= $(call my-dir) testFiles := \ AppAsLib_test.cpp \ + Asset_test.cpp \ AttributeFinder_test.cpp \ ByteBucketArray_test.cpp \ Config_test.cpp \ diff --git a/libs/androidfw/tests/Asset_test.cpp b/libs/androidfw/tests/Asset_test.cpp new file mode 100644 index 000000000000..45c8cef92918 --- /dev/null +++ b/libs/androidfw/tests/Asset_test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016 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 <androidfw/Asset.h> + +#include <gtest/gtest.h> + +using namespace android; + +TEST(AssetTest, FileAssetRegistersItself) { + const int32_t count = Asset::getGlobalCount(); + Asset* asset = new _FileAsset(); + EXPECT_EQ(count + 1, Asset::getGlobalCount()); + delete asset; + EXPECT_EQ(count, Asset::getGlobalCount()); +} + +TEST(AssetTest, CompressedAssetRegistersItself) { + const int32_t count = Asset::getGlobalCount(); + Asset* asset = new _CompressedAsset(); + EXPECT_EQ(count + 1, Asset::getGlobalCount()); + delete asset; + EXPECT_EQ(count, Asset::getGlobalCount()); +} diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 366ef716c109..cacfce16ab9a 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -3,6 +3,7 @@ include $(CLEAR_VARS) LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk HWUI_NEW_OPS := true +BUGREPORT_FONT_CACHE_USAGE := false # Enables fine-grained GLES error checking # If set to true, every GLES call is wrapped & error checked @@ -135,6 +136,13 @@ ifeq (true, $(HWUI_NEW_OPS)) endif +ifeq (true, $(BUGREPORT_FONT_CACHE_USAGE)) + hwui_src_files += \ + font/FontCacheHistoryTracker.cpp + hwui_cflags += -DBUGREPORT_FONT_CACHE_USAGE +endif + + ifndef HWUI_COMPILE_SYMBOLS hwui_cflags += -fvisibility=hidden endif diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 949ad450d5f7..a8ced9b2597b 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -21,6 +21,9 @@ #include "Properties.h" #include "renderstate/RenderState.h" #include "ShadowTessellator.h" +#ifdef BUGREPORT_FONT_CACHE_USAGE +#include "font/FontCacheHistoryTracker.h" +#endif #include "utils/GLUtils.h" #include <cutils/properties.h> @@ -195,12 +198,7 @@ void Caches::dumpMemoryUsage(String8 &log) { log.appendFormat(" PatchCache %8d / %8d\n", patchCache.getSize(), patchCache.getMaxSize()); - const uint32_t sizeA8 = fontRenderer.getFontRendererSize(GL_ALPHA); - const uint32_t sizeRGBA = fontRenderer.getFontRendererSize(GL_RGBA); - log.appendFormat(" FontRenderer A8 %8d / %8d\n", sizeA8, sizeA8); - log.appendFormat(" FontRenderer RGBA %8d / %8d\n", sizeRGBA, sizeRGBA); - log.appendFormat(" FontRenderer total %8d / %8d\n", sizeA8 + sizeRGBA, - sizeA8 + sizeRGBA); + fontRenderer.dumpMemoryUsage(log); log.appendFormat("Other:\n"); log.appendFormat(" FboCache %8d / %8d\n", @@ -213,11 +211,14 @@ void Caches::dumpMemoryUsage(String8 &log) { total += tessellationCache.getSize(); total += dropShadowCache.getSize(); total += patchCache.getSize(); - total += fontRenderer.getFontRendererSize(GL_ALPHA); - total += fontRenderer.getFontRendererSize(GL_RGBA); + total += fontRenderer.getSize(); log.appendFormat("Total memory usage:\n"); log.appendFormat(" %d bytes, %.2f MB\n", total, total / 1024.0f / 1024.0f); + +#ifdef BUGREPORT_FONT_CACHE_USAGE + fontRenderer.getFontRenderer().historyTracker().dump(log); +#endif } /////////////////////////////////////////////////////////////////////////////// diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 276c18d0d3f9..681cf55066b4 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -168,10 +168,17 @@ void FontRenderer::flushAllAndInvalidate() { for (uint32_t i = 0; i < mACacheTextures.size(); i++) { mACacheTextures[i]->init(); + +#ifdef BUGREPORT_FONT_CACHE_USAGE + mHistoryTracker.glyphsCleared(mACacheTextures[i]); +#endif } for (uint32_t i = 0; i < mRGBACacheTextures.size(); i++) { mRGBACacheTextures[i]->init(); +#ifdef BUGREPORT_FONT_CACHE_USAGE + mHistoryTracker.glyphsCleared(mRGBACacheTextures[i]); +#endif } mDrawn = false; @@ -183,6 +190,9 @@ void FontRenderer::flushLargeCaches(std::vector<CacheTexture*>& cacheTextures) { CacheTexture* cacheTexture = cacheTextures[i]; if (cacheTexture->getPixelBuffer()) { cacheTexture->init(); +#ifdef BUGREPORT_FONT_CACHE_USAGE + mHistoryTracker.glyphsCleared(cacheTexture); +#endif LruCache<Font::FontDescription, Font*>::Iterator it(mActiveFonts); while (it.next()) { it.value()->invalidateTextureCache(cacheTexture); @@ -385,6 +395,10 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp } cachedGlyph->mIsValid = true; + +#ifdef BUGREPORT_FONT_CACHE_USAGE + mHistoryTracker.glyphUploaded(cacheTexture, startX, startY, glyph.fWidth, glyph.fHeight); +#endif } CacheTexture* FontRenderer::createCacheTexture(int width, int height, GLenum format, @@ -747,19 +761,68 @@ static uint32_t calculateCacheSize(const std::vector<CacheTexture*>& cacheTextur return size; } -uint32_t FontRenderer::getCacheSize(GLenum format) const { +static uint32_t calculateFreeCacheSize(const std::vector<CacheTexture*>& cacheTextures) { + uint32_t size = 0; + for (uint32_t i = 0; i < cacheTextures.size(); i++) { + CacheTexture* cacheTexture = cacheTextures[i]; + if (cacheTexture && cacheTexture->getPixelBuffer()) { + size += cacheTexture->calculateFreeMemory(); + } + } + return size; +} + +const std::vector<CacheTexture*>& FontRenderer::cacheTexturesForFormat(GLenum format) const { switch (format) { case GL_ALPHA: { - return calculateCacheSize(mACacheTextures); + return mACacheTextures; } case GL_RGBA: { - return calculateCacheSize(mRGBACacheTextures); + return mRGBACacheTextures; } default: { - return 0; + LOG_ALWAYS_FATAL("Unsupported format: %d", format); + // Impossible to hit this, but the compiler doesn't know that + return *(new std::vector<CacheTexture*>()); } } } +static void dumpTextures(String8& log, const char* tag, + const std::vector<CacheTexture*>& cacheTextures) { + for (uint32_t i = 0; i < cacheTextures.size(); i++) { + CacheTexture* cacheTexture = cacheTextures[i]; + if (cacheTexture && cacheTexture->getPixelBuffer()) { + uint32_t free = cacheTexture->calculateFreeMemory(); + uint32_t total = cacheTexture->getPixelBuffer()->getSize(); + log.appendFormat(" %-4s texture %d %8d / %8d\n", tag, i, total - free, total); + } + } +} + +void FontRenderer::dumpMemoryUsage(String8& log) const { + const uint32_t sizeA8 = getCacheSize(GL_ALPHA); + const uint32_t usedA8 = sizeA8 - getFreeCacheSize(GL_ALPHA); + const uint32_t sizeRGBA = getCacheSize(GL_RGBA); + const uint32_t usedRGBA = sizeRGBA - getFreeCacheSize(GL_RGBA); + log.appendFormat(" FontRenderer A8 %8d / %8d\n", usedA8, sizeA8); + dumpTextures(log, "A8", cacheTexturesForFormat(GL_ALPHA)); + log.appendFormat(" FontRenderer RGBA %8d / %8d\n", usedRGBA, sizeRGBA); + dumpTextures(log, "RGBA", cacheTexturesForFormat(GL_RGBA)); + log.appendFormat(" FontRenderer total %8d / %8d\n", usedA8 + usedRGBA, sizeA8 + sizeRGBA); +} + +uint32_t FontRenderer::getCacheSize(GLenum format) const { + return calculateCacheSize(cacheTexturesForFormat(format)); +} + +uint32_t FontRenderer::getFreeCacheSize(GLenum format) const { + return calculateFreeCacheSize(cacheTexturesForFormat(format)); +} + +uint32_t FontRenderer::getSize() const { + return getCacheSize(GL_ALPHA) + getCacheSize(GL_RGBA); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index e10a81b8ccd8..504dce862f71 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -21,8 +21,12 @@ #include "font/CacheTexture.h" #include "font/CachedGlyphInfo.h" #include "font/Font.h" +#ifdef BUGREPORT_FONT_CACHE_USAGE +#include "font/FontCacheHistoryTracker.h" +#endif #include <utils/LruCache.h> +#include <utils/String8.h> #include <utils/StrongPointer.h> #include <SkPaint.h> @@ -132,7 +136,12 @@ public: mLinearFiltering = linearFiltering; } - uint32_t getCacheSize(GLenum format) const; + uint32_t getSize() const; + void dumpMemoryUsage(String8& log) const; + +#ifdef BUGREPORT_FONT_CACHE_USAGE + FontCacheHistoryTracker& historyTracker() { return mHistoryTracker; } +#endif private: friend class Font; @@ -175,6 +184,10 @@ private: mUploadTexture = true; } + const std::vector<CacheTexture*>& cacheTexturesForFormat(GLenum format) const; + uint32_t getCacheSize(GLenum format) const; + uint32_t getFreeCacheSize(GLenum format) const; + uint32_t mSmallCacheWidth; uint32_t mSmallCacheHeight; uint32_t mLargeCacheWidth; @@ -199,6 +212,10 @@ private: bool mLinearFiltering; +#ifdef BUGREPORT_FONT_CACHE_USAGE + FontCacheHistoryTracker mHistoryTracker; +#endif + #ifdef ANDROID_ENABLE_RENDERSCRIPT // RS constructs RSC::sp<RSC::RS> mRs; diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index 37d9d0e749e6..7524ba0dcea6 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -591,7 +591,7 @@ void FrameBuilder::deferArcOp(const ArcOp& op) { } static bool hasMergeableClip(const BakedOpState& state) { - return state.computedState.clipState + return !state.computedState.clipState || state.computedState.clipState->mode == ClipMode::Rectangle; } diff --git a/libs/hwui/GammaFontRenderer.h b/libs/hwui/GammaFontRenderer.h index 5813e7f717ee..bd27a1a72060 100644 --- a/libs/hwui/GammaFontRenderer.h +++ b/libs/hwui/GammaFontRenderer.h @@ -22,6 +22,8 @@ #include <SkPaint.h> +#include <utils/String8.h> + namespace android { namespace uirenderer { @@ -46,8 +48,16 @@ public: return *mRenderer; } - uint32_t getFontRendererSize(GLenum format) const { - return mRenderer ? mRenderer->getCacheSize(format) : 0; + void dumpMemoryUsage(String8& log) const { + if (mRenderer) { + mRenderer->dumpMemoryUsage(log); + } else { + log.appendFormat("FontRenderer doesn't exist.\n"); + } + } + + uint32_t getSize() const { + return mRenderer ? mRenderer->getSize() : 0; } void endPrecaching(); diff --git a/libs/hwui/PropertyValuesAnimatorSet.cpp b/libs/hwui/PropertyValuesAnimatorSet.cpp index 38fb70a92e43..e3258e3c1a48 100644 --- a/libs/hwui/PropertyValuesAnimatorSet.cpp +++ b/libs/hwui/PropertyValuesAnimatorSet.cpp @@ -46,8 +46,17 @@ PropertyValuesAnimatorSet::PropertyValuesAnimatorSet() void PropertyValuesAnimatorSet::onFinished(BaseRenderNodeAnimator* animator) { if (mOneShotListener.get()) { - mOneShotListener->onAnimationFinished(animator); + sp<AnimationListener> listener = std::move(mOneShotListener); + // Set the listener to nullptr before the onAnimationFinished callback, rather than after, + // for two reasons: + // 1) We need to prevent changes to mOneShotListener during the onAnimationFinished + // callback (specifically in AnimationListenerBridge::onAnimationFinished(...) from + // triggering dtor of the bridge and potentially unsafely re-entering + // AnimationListenerBridge::onAnimationFinished(...). + // 2) It's possible that there are changes to the listener during the callback, therefore + // we need to reset the listener before the callback rather than afterwards. mOneShotListener = nullptr; + listener->onAnimationFinished(animator); } } diff --git a/libs/hwui/Readback.cpp b/libs/hwui/Readback.cpp index 55f823dfe226..0ab247dc8052 100644 --- a/libs/hwui/Readback.cpp +++ b/libs/hwui/Readback.cpp @@ -136,7 +136,7 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs); if (sourceImage == EGL_NO_IMAGE_KHR) { - ALOGW("Error creating image (%#x)", eglGetError()); + ALOGW("eglCreateImageKHR failed (%#x)", eglGetError()); return CopyResult::UnknownError; } GLuint sourceTexId; @@ -147,7 +147,8 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, GLenum status = GL_NO_ERROR; while ((status = glGetError()) != GL_NO_ERROR) { - ALOGW("Error creating image (%#x)", status); + ALOGW("glEGLImageTargetTexture2DOES failed (%#x)", status); + eglDestroyImageKHR(display, sourceImage); return CopyResult::UnknownError; } @@ -183,6 +184,13 @@ CopyResult Readback::copySurfaceInto(renderthread::RenderThread& renderThread, caches.textureState().deleteTexture(texture); renderState.deleteFramebuffer(fbo); + sourceTexture.deleteTexture(); + // All we're flushing & finishing is the deletion of the texture since + // copyTextureInto already did a major flush & finish as an implicit + // part of glReadPixels, so this shouldn't pose any major stalls. + glFinish(); + eglDestroyImageKHR(display, sourceImage); + GL_CHECKPOINT(MODERATE); return CopyResult::Success; diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index ce67554645d1..c7289fce1fbf 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -329,9 +329,10 @@ void SkiaCanvas::restoreToCount(int restoreCount) { static inline SkCanvas::SaveLayerFlags layerFlags(SaveFlags::Flags flags) { SkCanvas::SaveLayerFlags layerFlags = 0; - if (!(flags & SaveFlags::HasAlphaLayer)) { - layerFlags |= SkCanvas::kIsOpaque_SaveLayerFlag; - } + // We intentionally ignore the SaveFlags::HasAlphaLayer and + // SkCanvas::kIsOpaque_SaveLayerFlag flags because HWUI ignores it + // and our Android client may use it incorrectly. + // In Skia, this flag is purely for performance optimization. if (!(flags & SaveFlags::ClipToLayer)) { layerFlags |= SkCanvas::kDontClipToLayer_Legacy_SaveLayerFlag; diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index 760d814f27a8..cc96a137c306 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -942,9 +942,13 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength AlphaVertex::set(&shadowVertices[vertexBufferIndex++], newPenumbra[i].x, newPenumbra[i].y, PENUMBRA_ALPHA); } + // Since the umbra can be a faked one when the occluder is too high, the umbra should be lighter + // in this case. + float scaledUmbraAlpha = UMBRA_ALPHA * shadowStrengthScale; + for (int i = 0; i < umbraLength; i++) { AlphaVertex::set(&shadowVertices[vertexBufferIndex++], umbra[i].x, umbra[i].y, - UMBRA_ALPHA); + scaledUmbraAlpha); } for (int i = 0; i < verticesPairIndex; i++) { @@ -984,14 +988,14 @@ void SpotShadow::generateTriangleStrip(bool isCasterOpaque, float shadowStrength indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = vertexBufferIndex; AlphaVertex::set(&shadowVertices[vertexBufferIndex++], - closerVertex.x, closerVertex.y, UMBRA_ALPHA); + closerVertex.x, closerVertex.y, scaledUmbraAlpha); } } 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, UMBRA_ALPHA); + centroid.y, scaledUmbraAlpha); for (int i = 0; i < umbraLength; i++) { indexBuffer[indexBufferIndex++] = newPenumbraLength + i; indexBuffer[indexBufferIndex++] = lastCentroidIndex; diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp index 2b7994139641..aeee66106fb3 100644 --- a/libs/hwui/VectorDrawable.cpp +++ b/libs/hwui/VectorDrawable.cpp @@ -202,7 +202,9 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca if (properties.getFillGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getFillAlpha())); SkShader* newShader = properties.getFillGradient()->newWithLocalMatrix(matrix); - paint.setShader(newShader); + // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to + // remove the extra ref so that the ref count is correctly managed. + paint.setShader(newShader)->unref(); needsFill = true; } else if (properties.getFillColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getFillColor(), properties.getFillAlpha())); @@ -222,7 +224,9 @@ void FullPath::drawPath(SkCanvas* outCanvas, SkPath& renderPath, float strokeSca if (properties.getStrokeGradient() != nullptr) { paint.setColor(applyAlpha(SK_ColorBLACK, properties.getStrokeAlpha())); SkShader* newShader = properties.getStrokeGradient()->newWithLocalMatrix(matrix); - paint.setShader(newShader); + // newWithLocalMatrix(...) creates a new SkShader and returns a bare pointer. We need to + // remove the extra ref so that the ref count is correctly managed. + paint.setShader(newShader)->unref(); needsStroke = true; } else if (properties.getStrokeColor() != SK_ColorTRANSPARENT) { paint.setColor(applyAlpha(properties.getStrokeColor(), properties.getStrokeAlpha())); diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp index 8ba4761c1b2e..4b13814bfdc6 100644 --- a/libs/hwui/font/CacheTexture.cpp +++ b/libs/hwui/font/CacheTexture.cpp @@ -324,5 +324,17 @@ bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_ return false; } +uint32_t CacheTexture::calculateFreeMemory() const { + CacheBlock* cacheBlock = mCacheBlocks; + uint32_t free = 0; + // currently only two formats are supported: GL_ALPHA or GL_RGBA; + uint32_t bpp = mFormat == GL_RGBA ? 4 : 1; + while (cacheBlock) { + free += bpp * cacheBlock->mWidth * cacheBlock->mHeight; + cacheBlock = cacheBlock->mNext; + } + return free; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h index 4dfb41dafcc7..6750a8ae11cf 100644 --- a/libs/hwui/font/CacheTexture.h +++ b/libs/hwui/font/CacheTexture.h @@ -178,6 +178,8 @@ public: return mCurrentQuad == mMaxQuadCount; } + uint32_t calculateFreeMemory() const; + private: void setDirty(bool dirty); diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 8e04c8715f62..a95454a4c010 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -408,9 +408,15 @@ void Font::render(const SkPaint* paint, const glyph_t* glyphs, if (cachedGlyph->mIsValid && cachedGlyph->mCacheTexture) { int penX = x + (int) roundf(positions[(glyphsCount << 1)]); int penY = y + (int) roundf(positions[(glyphsCount << 1) + 1]); - +#ifdef BUGREPORT_FONT_CACHE_USAGE + mState->historyTracker().glyphRendered(cachedGlyph, penX, penY); +#endif (*this.*render)(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH, bounds, positions); + } else { +#ifdef BUGREPORT_FONT_CACHE_USAGE + mState->historyTracker().glyphRendered(cachedGlyph, -1, -1); +#endif } glyphsCount++; diff --git a/libs/hwui/font/FontCacheHistoryTracker.cpp b/libs/hwui/font/FontCacheHistoryTracker.cpp new file mode 100644 index 000000000000..a2bfb27535e5 --- /dev/null +++ b/libs/hwui/font/FontCacheHistoryTracker.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2016 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 "FontCacheHistoryTracker.h" + +#include "CachedGlyphInfo.h" +#include "CacheTexture.h" + +namespace android { +namespace uirenderer { + +void FontCacheHistoryTracker::dumpCachedGlyph(String8& log, const CachedGlyph& glyph) { + log.appendFormat("glyph (texture %p, position: (%d, %d), size: %dx%d, gen: %d)", glyph.texture, + glyph.startX, glyph.startY, glyph.bitmapW, glyph.bitmapH, glyph.generation); +} + +void FontCacheHistoryTracker::dumpRenderEntry(String8& log, const RenderEntry& entry) { + if (entry.penX == -1 && entry.penY == -1) { + log.appendFormat(" glyph skipped in gen: %d\n", entry.glyph.generation); + } else { + log.appendFormat(" rendered "); + dumpCachedGlyph(log, entry.glyph); + log.appendFormat(" at (%d, %d)\n", entry.penX, entry.penY); + } +} + +void FontCacheHistoryTracker::dumpUploadEntry(String8& log, const CachedGlyph& glyph) { + if (glyph.bitmapW == 0 && glyph.bitmapH == 0) { + log.appendFormat(" cleared cachetexture %p in gen %d\n", glyph.texture, + glyph.generation); + } else { + log.appendFormat(" uploaded "); + dumpCachedGlyph(log, glyph); + log.appendFormat("\n"); + } +} + +void FontCacheHistoryTracker::dump(String8& log) const { + log.appendFormat("FontCacheHistory: \n"); + log.appendFormat(" Upload history: \n"); + for (size_t i = 0; i < mUploadHistory.size(); i++) { + dumpUploadEntry(log, mUploadHistory[i]); + } + log.appendFormat(" Render history: \n"); + for (size_t i = 0; i < mRenderHistory.size(); i++) { + dumpRenderEntry(log, mRenderHistory[i]); + } +} + +void FontCacheHistoryTracker::glyphRendered(CachedGlyphInfo* glyphInfo, int penX, int penY) { + RenderEntry& entry = mRenderHistory.next(); + entry.glyph.generation = generation; + entry.glyph.texture = glyphInfo->mCacheTexture; + entry.glyph.startX = glyphInfo->mStartX; + entry.glyph.startY = glyphInfo->mStartY; + entry.glyph.bitmapW = glyphInfo->mBitmapWidth; + entry.glyph.bitmapH = glyphInfo->mBitmapHeight; + entry.penX = penX; + entry.penY = penY; +} + +void FontCacheHistoryTracker::glyphUploaded(CacheTexture* texture, uint32_t x, uint32_t y, + uint16_t glyphW, uint16_t glyphH) { + CachedGlyph& glyph = mUploadHistory.next(); + glyph.generation = generation; + glyph.texture = texture; + glyph.startX = x; + glyph.startY = y; + glyph.bitmapW = glyphW; + glyph.bitmapH = glyphH; +} + +void FontCacheHistoryTracker::glyphsCleared(CacheTexture* texture) { + CachedGlyph& glyph = mUploadHistory.next(); + glyph.generation = generation; + glyph.texture = texture; + glyph.startX = 0; + glyph.startY = 0; + glyph.bitmapW = 0; + glyph.bitmapH = 0; +} + +void FontCacheHistoryTracker::frameCompleted() { + generation++; +} +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/font/FontCacheHistoryTracker.h b/libs/hwui/font/FontCacheHistoryTracker.h new file mode 100644 index 000000000000..f1d9b9f10dc0 --- /dev/null +++ b/libs/hwui/font/FontCacheHistoryTracker.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once +#include "../utils/RingBuffer.h" + +#include <utils/String8.h> + +namespace android { +namespace uirenderer { + +class CacheTexture; +struct CachedGlyphInfo; + +// Tracks glyph uploads and recent rendered/skipped glyphs, so it can give an idea +// what a missing character is: skipped glyph, wrong coordinates in cache texture etc. +class FontCacheHistoryTracker { +public: + void glyphRendered(CachedGlyphInfo*, int penX, int penY); + void glyphUploaded(CacheTexture*, uint32_t x, uint32_t y, uint16_t glyphW, uint16_t glyphH); + void glyphsCleared(CacheTexture*); + void frameCompleted(); + + void dump(String8& log) const; +private: + struct CachedGlyph { + void* texture; + uint16_t generation; + uint16_t startX; + uint16_t startY; + uint16_t bitmapW; + uint16_t bitmapH; + }; + + struct RenderEntry { + CachedGlyph glyph; + int penX; + int penY; + }; + + static void dumpCachedGlyph(String8& log, const CachedGlyph& glyph); + static void dumpRenderEntry(String8& log, const RenderEntry& entry); + static void dumpUploadEntry(String8& log, const CachedGlyph& glyph); + + RingBuffer<RenderEntry, 300> mRenderHistory; + RingBuffer<CachedGlyph, 120> mUploadHistory; + uint16_t generation = 0; +}; + +}; // namespace uirenderer +}; // namespace android
\ No newline at end of file diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index dcaec428449c..975ac8368e3d 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -608,6 +608,10 @@ void CanvasContext::draw() { } GpuMemoryTracker::onFrameCompleted(); +#ifdef BUGREPORT_FONT_CACHE_USAGE + caches.fontRenderer.getFontRenderer().historyTracker().frameCompleted(); +#endif + } // Called by choreographer to do an RT-driven animation @@ -633,6 +637,9 @@ void CanvasContext::prepareAndDraw(RenderNode* node) { prepareTree(info, frameInfo, systemTime(CLOCK_MONOTONIC), node); if (info.out.canDrawThisFrame) { draw(); + } else { + // wait on fences so tasks don't overlap next frame + waitOnFences(); } } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 3eef29b24473..e1821751b57f 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -168,6 +168,8 @@ public: ANDROID_API int64_t getFrameNumber(); + void waitOnFences(); + private: friend class RegisterFrameCallbackTask; // TODO: Replace with something better for layer & other GL object @@ -178,8 +180,6 @@ private: void freePrefetchedLayers(TreeObserver* observer); - void waitOnFences(); - bool isSwapChainStuffed(); EGLint mLastFrameWidth = 0; diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index c9c07b3df292..e3b6dc6fd9fe 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -104,6 +104,9 @@ void DrawFrameTask::run() { if (CC_LIKELY(canDrawThisFrame)) { context->draw(); + } else { + // wait on fences so tasks don't overlap next frame + context->waitOnFences(); } if (!canUnblockUiThread) { diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp index 53dbede2f8e1..e2dc3a0a2c66 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -477,6 +477,35 @@ RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { EXPECT_EQ(4, renderer.getIndex()); } +RENDERTHREAD_TEST(FrameBuilder, regionClipStopsMerge) { + class RegionClipStopsMergeTestRenderer : public TestRendererBase { + public: + void onTextOp(const TextOp& op, const BakedOpState& state) override { mIndex++; } + }; + auto node = TestUtils::createNode(0, 0, 400, 400, + [](RenderProperties& props, TestCanvas& canvas) { + SkPath path; + path.addCircle(200, 200, 200, SkPath::kCW_Direction); + canvas.save(SaveFlags::MatrixClip); + canvas.clipPath(&path, SkRegion::kIntersect_Op); + SkPaint paint; + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); + paint.setAntiAlias(true); + paint.setTextSize(50); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 100); + TestUtils::drawUtf8ToCanvas(&canvas, "Test string1", paint, 100, 200); + canvas.restore(); + }); + + FrameBuilder frameBuilder(SkRect::MakeWH(400, 400), 400, 400, + sLightGeometry, Caches::getInstance()); + frameBuilder.deferRenderNode(*TestUtils::getSyncedNode(node)); + + RegionClipStopsMergeTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(2, renderer.getIndex()); +} + RENDERTHREAD_TEST(FrameBuilder, textMerging) { class TextMergingTestRenderer : public TestRendererBase { public: diff --git a/libs/hwui/tests/unit/VectorDrawableTests.cpp b/libs/hwui/tests/unit/VectorDrawableTests.cpp index 83b485fa705e..8e0d3ee572a6 100644 --- a/libs/hwui/tests/unit/VectorDrawableTests.cpp +++ b/libs/hwui/tests/unit/VectorDrawableTests.cpp @@ -426,5 +426,49 @@ TEST(VectorDrawable, groupProperties) { EXPECT_EQ(1.0f, properties->getPivotY()); } + +static SkShader* createShader(bool* isDestroyed) { + class TestShader : public SkShader { + public: + TestShader(bool* isDestroyed) : SkShader(), mDestroyed(isDestroyed) { + } + ~TestShader() { + *mDestroyed = true; + } + + Factory getFactory() const override { return nullptr; } + private: + bool* mDestroyed; + }; + return new TestShader(isDestroyed); +} + +TEST(VectorDrawable, drawPathWithoutIncrementingShaderRefCount) { + VectorDrawable::FullPath path("m1 1", 4); + SkBitmap bitmap; + SkImageInfo info = SkImageInfo::Make(5, 5, kN32_SkColorType, kPremul_SkAlphaType); + bitmap.setInfo(info); + bitmap.allocPixels(info); + SkCanvas canvas(bitmap); + + bool shaderIsDestroyed = false; + + // Initial ref count is 1 + SkShader* shader = createShader(&shaderIsDestroyed); + + // Setting the fill gradient increments the ref count of the shader by 1 + path.mutateStagingProperties()->setFillGradient(shader); + path.draw(&canvas, SkMatrix::I(), 1.0f, 1.0f, true); + // Resetting the fill gradient decrements the ref count of the shader by 1 + path.mutateStagingProperties()->setFillGradient(nullptr); + + // Expect ref count to be 1 again, i.e. nothing else to have a ref to the shader now. Unref() + // again should bring the ref count to zero and consequently trigger detor. + shader->unref(); + + // Verify that detor is called. + EXPECT_TRUE(shaderIsDestroyed); +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp index 27193b743379..abef66f9ecd9 100644 --- a/libs/input/PointerController.cpp +++ b/libs/input/PointerController.cpp @@ -36,6 +36,29 @@ namespace android { +// --- WeakLooperCallback --- + +class WeakLooperCallback: public LooperCallback { +protected: + virtual ~WeakLooperCallback() { } + +public: + WeakLooperCallback(const wp<LooperCallback>& callback) : + mCallback(callback) { + } + + virtual int handleEvent(int fd, int events, void* data) { + sp<LooperCallback> callback = mCallback.promote(); + if (callback != NULL) { + return callback->handleEvent(fd, events, data); + } + return 0; // the client is gone, remove the callback + } + +private: + wp<LooperCallback> mCallback; +}; + // --- PointerController --- // Time to wait before starting the fade when the pointer is inactive. @@ -57,10 +80,11 @@ PointerController::PointerController(const sp<PointerControllerPolicyInterface>& const sp<Looper>& looper, const sp<SpriteController>& spriteController) : mPolicy(policy), mLooper(looper), mSpriteController(spriteController) { mHandler = new WeakMessageHandler(this); + mCallback = new WeakLooperCallback(this); if (mDisplayEventReceiver.initCheck() == NO_ERROR) { mLooper->addFd(mDisplayEventReceiver.getFd(), Looper::POLL_CALLBACK, - Looper::EVENT_INPUT, this, nullptr); + Looper::EVENT_INPUT, mCallback, nullptr); } else { ALOGE("Failed to initialize DisplayEventReceiver."); } diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h index 99292d7ca8a6..4794f3da824c 100644 --- a/libs/input/PointerController.h +++ b/libs/input/PointerController.h @@ -144,6 +144,7 @@ private: sp<Looper> mLooper; sp<SpriteController> mSpriteController; sp<WeakMessageHandler> mHandler; + sp<LooperCallback> mCallback; DisplayEventReceiver mDisplayEventReceiver; |