diff options
author | Romain Guy <romainguy@google.com> | 2013-03-15 19:06:39 -0700 |
---|---|---|
committer | Romain Guy <romainguy@google.com> | 2013-03-18 18:32:17 -0700 |
commit | c46d07a29e94807e768f8b162ce9f77a88ba6f46 (patch) | |
tree | 9b6aec5f5dc1d73b6cd9d583d5c5c78285480070 /libs | |
parent | 338b18844434379de54050ff582d36ff6da3ba11 (diff) | |
download | frameworks_base-c46d07a29e94807e768f8b162ce9f77a88ba6f46.zip frameworks_base-c46d07a29e94807e768f8b162ce9f77a88ba6f46.tar.gz frameworks_base-c46d07a29e94807e768f8b162ce9f77a88ba6f46.tar.bz2 |
Merge all shapes/paths caches to PathCache
This change will greatly simplify the multi-threading of all
shape types.
This change also uses PathTessellator to render convex paths.
Change-Id: I4e65bc95c9d24ecae2183b72204de5c2dfb6ada4
Diffstat (limited to 'libs')
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/Caches.cpp | 20 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 6 | ||||
-rw-r--r-- | libs/hwui/Debug.h | 2 | ||||
-rw-r--r-- | libs/hwui/DeferredDisplayList.cpp | 2 | ||||
-rw-r--r-- | libs/hwui/DisplayList.cpp | 6 | ||||
-rw-r--r-- | libs/hwui/DisplayListRenderer.cpp | 1 | ||||
-rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 18 | ||||
-rw-r--r-- | libs/hwui/PathCache.cpp | 443 | ||||
-rw-r--r-- | libs/hwui/PathCache.h | 249 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 4 | ||||
-rw-r--r-- | libs/hwui/ShapeCache.cpp | 168 | ||||
-rw-r--r-- | libs/hwui/ShapeCache.h | 816 |
13 files changed, 661 insertions, 1075 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 1618110..06e658d 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -34,7 +34,6 @@ ifeq ($(USE_OPENGL_RENDERER),true) ProgramCache.cpp \ RenderBufferCache.cpp \ ResourceCache.cpp \ - ShapeCache.cpp \ SkiaColorFilter.cpp \ SkiaShader.cpp \ Snapshot.cpp \ diff --git a/libs/hwui/Caches.cpp b/libs/hwui/Caches.cpp index 4642a4f..dc3a4e2 100644 --- a/libs/hwui/Caches.cpp +++ b/libs/hwui/Caches.cpp @@ -224,16 +224,6 @@ void Caches::dumpMemoryUsage(String8 &log) { gradientCache.getSize(), gradientCache.getMaxSize()); log.appendFormat(" PathCache %8d / %8d\n", pathCache.getSize(), pathCache.getMaxSize()); - log.appendFormat(" CircleShapeCache %8d / %8d\n", - circleShapeCache.getSize(), circleShapeCache.getMaxSize()); - log.appendFormat(" OvalShapeCache %8d / %8d\n", - ovalShapeCache.getSize(), ovalShapeCache.getMaxSize()); - log.appendFormat(" RoundRectShapeCache %8d / %8d\n", - roundRectShapeCache.getSize(), roundRectShapeCache.getMaxSize()); - log.appendFormat(" RectShapeCache %8d / %8d\n", - rectShapeCache.getSize(), rectShapeCache.getMaxSize()); - log.appendFormat(" ArcShapeCache %8d / %8d\n", - arcShapeCache.getSize(), arcShapeCache.getMaxSize()); log.appendFormat(" TextDropShadowCache %8d / %8d\n", dropShadowCache.getSize(), dropShadowCache.getMaxSize()); for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) { @@ -253,11 +243,6 @@ void Caches::dumpMemoryUsage(String8 &log) { total += gradientCache.getSize(); total += pathCache.getSize(); total += dropShadowCache.getSize(); - total += roundRectShapeCache.getSize(); - total += circleShapeCache.getSize(); - total += ovalShapeCache.getSize(); - total += rectShapeCache.getSize(); - total += arcShapeCache.getSize(); for (uint32_t i = 0; i < fontRenderer->getFontRendererCount(); i++) { total += fontRenderer->getFontRendererSize(i); } @@ -325,11 +310,6 @@ void Caches::flush(FlushMode mode) { fontRenderer->flush(); textureCache.flush(); pathCache.clear(); - roundRectShapeCache.clear(); - circleShapeCache.clear(); - ovalShapeCache.clear(); - rectShapeCache.clear(); - arcShapeCache.clear(); // fall through case kFlushMode_Layers: layerCache.clear(); diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index dc32a7e..63836c1 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -36,7 +36,6 @@ #include "GradientCache.h" #include "PatchCache.h" #include "ProgramCache.h" -#include "ShapeCache.h" #include "PathCache.h" #include "TextDropShadowCache.h" #include "FboCache.h" @@ -269,11 +268,6 @@ public: GradientCache gradientCache; ProgramCache programCache; PathCache pathCache; - RoundRectShapeCache roundRectShapeCache; - CircleShapeCache circleShapeCache; - OvalShapeCache ovalShapeCache; - RectShapeCache rectShapeCache; - ArcShapeCache arcShapeCache; PatchCache patchCache; TextDropShadowCache dropShadowCache; FboCache fboCache; diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 773fe82..46beb94 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -64,7 +64,7 @@ #define DEBUG_PATCHES_EMPTY_VERTICES 0 // Turn on to display debug info about shapes -#define DEBUG_SHAPES 0 +#define DEBUG_PATHS 0 // Turn on to display debug info about textures #define DEBUG_TEXTURES 0 diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp index 8455545..5c5bf45 100644 --- a/libs/hwui/DeferredDisplayList.cpp +++ b/libs/hwui/DeferredDisplayList.cpp @@ -17,6 +17,8 @@ #define LOG_TAG "OpenGLRenderer" #define ATRACE_TAG ATRACE_TAG_VIEW +#include <SkCanvas.h> + #include <utils/Trace.h> #include "Debug.h" diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp index 4743f58..4944fe8 100644 --- a/libs/hwui/DisplayList.cpp +++ b/libs/hwui/DisplayList.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <SkCanvas.h> + #include "Debug.h" #include "DisplayList.h" #include "DisplayListOp.h" @@ -121,9 +123,7 @@ void DisplayList::clearResources() { } for (size_t i = 0; i < mPaths.size(); i++) { - SkPath* path = mPaths.itemAt(i); - caches.pathCache.remove(path); - delete path; + delete mPaths.itemAt(i); } for (size_t i = 0; i < mMatrices.size(); i++) { diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 11a655e..8b5b54c 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "OpenGLRenderer" #include <SkCamera.h> +#include <SkCanvas.h> #include <private/hwui/DrawGlInfo.h> diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e576f76..c310c4c 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -2257,9 +2257,11 @@ status_t OpenGLRenderer::drawConvexPath(const SkPath& path, SkPaint* paint) { // TODO: try clipping large paths to viewport PathTessellator::tessellatePath(path, paint, mSnapshot->transform, vertexBuffer); - SkRect bounds = path.getBounds(); - PathTessellator::expandBoundsForStroke(bounds, paint, false); - dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + if (hasLayer()) { + SkRect bounds = path.getBounds(); + PathTessellator::expandBoundsForStroke(bounds, paint, false); + dirtyLayer(bounds.fLeft, bounds.fTop, bounds.fRight, bounds.fBottom, currentTransform()); + } return drawVertexBuffer(vertexBuffer, paint); } @@ -2389,7 +2391,7 @@ status_t OpenGLRenderer::drawRoundRect(float left, float top, float right, float if (p->getPathEffect() != 0) { mCaches.activeTexture(0); - const PathTexture* texture = mCaches.roundRectShapeCache.getRoundRect( + const PathTexture* texture = mCaches.pathCache.getRoundRect( right - left, bottom - top, rx, ry, p); return drawShape(left, top, texture, p); } @@ -2413,7 +2415,7 @@ status_t OpenGLRenderer::drawCircle(float x, float y, float radius, SkPaint* p) } if (p->getPathEffect() != 0) { mCaches.activeTexture(0); - const PathTexture* texture = mCaches.circleShapeCache.getCircle(radius, p); + const PathTexture* texture = mCaches.pathCache.getCircle(radius, p); return drawShape(x - radius, y - radius, texture, p); } @@ -2434,7 +2436,7 @@ status_t OpenGLRenderer::drawOval(float left, float top, float right, float bott if (p->getPathEffect() != 0) { mCaches.activeTexture(0); - const PathTexture* texture = mCaches.ovalShapeCache.getOval(right - left, bottom - top, p); + const PathTexture* texture = mCaches.pathCache.getOval(right - left, bottom - top, p); return drawShape(left, top, texture, p); } @@ -2460,7 +2462,7 @@ status_t OpenGLRenderer::drawArc(float left, float top, float right, float botto // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180) if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) { mCaches.activeTexture(0); - const PathTexture* texture = mCaches.arcShapeCache.getArc(right - left, bottom - top, + const PathTexture* texture = mCaches.pathCache.getArc(right - left, bottom - top, startAngle, sweepAngle, useCenter, p); return drawShape(left, top, texture, p); } @@ -2495,7 +2497,7 @@ status_t OpenGLRenderer::drawRect(float left, float top, float right, float bott p->getStrokeMiter() != SkPaintDefaults_MiterLimit) { mCaches.activeTexture(0); const PathTexture* texture = - mCaches.rectShapeCache.getRect(right - left, bottom - top, p); + mCaches.pathCache.getRect(right - left, bottom - top, p); return drawShape(left, top, texture, p); } diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index fb687cd..490c22a 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 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. @@ -15,19 +15,301 @@ */ #define LOG_TAG "OpenGLRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW -#include <utils/Mutex.h> +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkPaint.h> +#include <SkPath.h> +#include <SkRect.h> -#include <sys/sysinfo.h> +#include <utils/JenkinsHash.h> +#include <utils/Trace.h> #include "Caches.h" #include "PathCache.h" -#include "Properties.h" + +#include "thread/Signal.h" +#include "thread/Task.h" +#include "thread/TaskProcessor.h" namespace android { namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// +// Cache entries +/////////////////////////////////////////////////////////////////////////////// + +PathDescription::PathDescription(): + type(kShapeNone), + join(SkPaint::kDefault_Join), + cap(SkPaint::kDefault_Cap), + style(SkPaint::kFill_Style), + miter(4.0f), + strokeWidth(1.0f), + pathEffect(NULL) { + memset(&shape, 0, sizeof(Shape)); +} + +PathDescription::PathDescription(ShapeType type, SkPaint* paint): + type(type), + join(paint->getStrokeJoin()), + cap(paint->getStrokeCap()), + style(paint->getStyle()), + miter(paint->getStrokeMiter()), + strokeWidth(paint->getStrokeWidth()), + pathEffect(paint->getPathEffect()) { + memset(&shape, 0, sizeof(Shape)); +} + +hash_t PathDescription::hash() const { + uint32_t hash = JenkinsHashMix(0, type); + hash = JenkinsHashMix(hash, join); + hash = JenkinsHashMix(hash, cap); + hash = JenkinsHashMix(hash, style); + hash = JenkinsHashMix(hash, android::hash_type(miter)); + hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); + hash = JenkinsHashMix(hash, android::hash_type(pathEffect)); + hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape)); + return JenkinsHashWhiten(hash); +} + +int PathDescription::compare(const PathDescription& rhs) const { + return memcmp(this, &rhs, sizeof(PathDescription)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Utilities +/////////////////////////////////////////////////////////////////////////////// + +bool PathCache::canDrawAsConvexPath(SkPath* path, SkPaint* paint) { + // NOTE: This should only be used after PathTessellator handles joins properly + return paint->getPathEffect() == NULL && path->getConvexity() == SkPath::kConvex_Convexity; +} + +void PathCache::computePathBounds(const SkPath* path, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { + const SkRect& bounds = path->getBounds(); + PathCache::computeBounds(bounds, paint, left, top, offset, width, height); +} + +void PathCache::computeBounds(const SkRect& bounds, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { + const float pathWidth = fmax(bounds.width(), 1.0f); + const float pathHeight = fmax(bounds.height(), 1.0f); + + left = bounds.fLeft; + top = bounds.fTop; + + offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); + + width = uint32_t(pathWidth + offset * 2.0 + 0.5); + height = uint32_t(pathHeight + offset * 2.0 + 0.5); +} + +static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { + bitmap.setConfig(SkBitmap::kA8_Config, width, height); + bitmap.allocPixels(); + bitmap.eraseColor(0); +} + +static void initPaint(SkPaint& paint) { + // Make sure the paint is opaque, color, alpha, filter, etc. + // will be applied later when compositing the alpha8 texture + paint.setColor(0xff000000); + paint.setAlpha(255); + paint.setColorFilter(NULL); + paint.setMaskFilter(NULL); + paint.setShader(NULL); + SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); + SkSafeUnref(paint.setXfermode(mode)); +} + +static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, + float left, float top, float offset, uint32_t width, uint32_t height) { + initBitmap(bitmap, width, height); + + SkPaint pathPaint(*paint); + initPaint(pathPaint); + + SkCanvas canvas(bitmap); + canvas.translate(-left + offset, -top + offset); + canvas.drawPath(*path, pathPaint); +} + +static PathTexture* createTexture(float left, float top, float offset, + uint32_t width, uint32_t height, uint32_t id) { + PathTexture* texture = new PathTexture(); + texture->left = left; + texture->top = top; + texture->offset = offset; + texture->width = width; + texture->height = height; + texture->generation = id; + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Cache constructor/destructor +/////////////////////////////////////////////////////////////////////////////// + +PathCache::PathCache(): + mCache(LruCache<PathDescription, PathTexture*>::kUnlimitedCapacity), + mSize(0), mMaxSize(MB(DEFAULT_PATH_CACHE_SIZE)) { + char property[PROPERTY_VALUE_MAX]; + if (property_get(PROPERTY_PATH_CACHE_SIZE, property, NULL) > 0) { + INIT_LOGD(" Setting %s cache size to %sMB", name, property); + setMaxSize(MB(atof(property))); + } else { + INIT_LOGD(" Using default %s cache size of %.2fMB", name, DEFAULT_PATH_CACHE_SIZE); + } + init(); +} + +PathCache::~PathCache() { + mCache.clear(); +} + +void PathCache::init() { + mCache.setOnEntryRemovedListener(this); + + GLint maxTextureSize; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + mMaxTextureSize = maxTextureSize; + + mDebugEnabled = readDebugLevel() & kDebugCaches; +} + +/////////////////////////////////////////////////////////////////////////////// +// Size management +/////////////////////////////////////////////////////////////////////////////// + +uint32_t PathCache::getSize() { + return mSize; +} + +uint32_t PathCache::getMaxSize() { + return mMaxSize; +} + +void PathCache::setMaxSize(uint32_t maxSize) { + mMaxSize = maxSize; + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Callbacks +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::operator()(PathDescription& entry, PathTexture*& texture) { + removeTexture(texture); +} + +/////////////////////////////////////////////////////////////////////////////// +// Caching +/////////////////////////////////////////////////////////////////////////////// + +void PathCache::removeTexture(PathTexture* texture) { + if (texture) { + const uint32_t size = texture->width * texture->height; + mSize -= size; + + PATH_LOGD("PathCache::delete name, size, mSize = %d, %d, %d", + texture->id, size, mSize); + if (mDebugEnabled) { + ALOGD("Shape deleted, size = %d", size); + } + + if (texture->id) { + glDeleteTextures(1, &texture->id); + } + delete texture; + } +} + +void PathCache::purgeCache(uint32_t width, uint32_t height) { + const uint32_t size = width * height; + // Don't even try to cache a bitmap that's bigger than the cache + if (size < mMaxSize) { + while (mSize + size > mMaxSize) { + mCache.removeOldest(); + } + } +} + +void PathCache::trim() { + while (mSize > mMaxSize) { + mCache.removeOldest(); + } +} + +PathTexture* PathCache::addTexture(const PathDescription& entry, const SkPath *path, + const SkPaint* paint) { + ATRACE_CALL(); + + float left, top, offset; + uint32_t width, height; + computePathBounds(path, paint, left, top, offset, width, height); + + if (!checkTextureSize(width, height)) return NULL; + + purgeCache(width, height); + + SkBitmap bitmap; + drawPath(path, paint, bitmap, left, top, offset, width, height); + + PathTexture* texture = createTexture(left, top, offset, width, height, + path->getGenerationID()); + addTexture(entry, &bitmap, texture); + + return texture; +} + +void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) { + generateTexture(*bitmap, texture); + + uint32_t size = texture->width * texture->height; + if (size < mMaxSize) { + mSize += size; + PATH_LOGD("PathCache::get/create: name, size, mSize = %d, %d, %d", + texture->id, size, mSize); + if (mDebugEnabled) { + ALOGD("Shape created, size = %d", size); + } + mCache.put(entry, texture); + } else { + texture->cleanup = true; + } +} + +void PathCache::clear() { + mCache.clear(); +} + +void PathCache::generateTexture(SkBitmap& bitmap, Texture* texture) { + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + ALOGE("Cannot generate texture from bitmap"); + return; + } + + glGenTextures(1, &texture->id); + + glBindTexture(GL_TEXTURE_2D, texture->id); + // Textures are Alpha8 + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + texture->blend = true; + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); + + texture->setFilter(GL_LINEAR); + texture->setWrap(GL_CLAMP_TO_EDGE); +} + +/////////////////////////////////////////////////////////////////////////////// // Path precaching /////////////////////////////////////////////////////////////////////////////// @@ -52,7 +334,7 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { if (width <= mMaxTextureSize && height <= mMaxTextureSize) { SkBitmap* bitmap = new SkBitmap(); - PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); t->setResult(bitmap); } else { texture->width = 0; @@ -62,23 +344,17 @@ void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { } /////////////////////////////////////////////////////////////////////////////// -// Path cache +// Paths /////////////////////////////////////////////////////////////////////////////// -PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", - PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) { -} - -PathCache::~PathCache() { -} - void PathCache::remove(SkPath* path) { - Vector<PathCacheEntry> pathsToRemove; - LruCache<PathCacheEntry, PathTexture*>::Iterator i(mCache); + Vector<PathDescription> pathsToRemove; + LruCache<PathDescription, PathTexture*>::Iterator i(mCache); while (i.next()) { - const PathCacheEntry& key = i.key(); - if (key.path == path || key.path == path->getSourcePath()) { + const PathDescription& key = i.key(); + if (key.type == kShapePath && + (key.shape.path.mPath == path || key.shape.path.mPath == path->getSourcePath())) { pathsToRemove.push(key); } } @@ -121,7 +397,9 @@ static SkPath* getSourcePath(SkPath* path) { PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { path = getSourcePath(path); - PathCacheEntry entry(path, paint); + PathDescription entry(kShapePath, paint); + entry.shape.path.mPath = path; + PathTexture* texture = mCache.get(entry); if (!texture) { @@ -159,7 +437,9 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { path = getSourcePath(path); - PathCacheEntry entry(path, paint); + PathDescription entry(kShapePath, paint); + entry.shape.path.mPath = path; + PathTexture* texture = mCache.get(entry); bool generate = false; @@ -193,5 +473,130 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { } } +/////////////////////////////////////////////////////////////////////////////// +// Rounded rects +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getRoundRect(float width, float height, + float rx, float ry, SkPaint* paint) { + PathDescription entry(kShapeRoundRect, paint); + entry.shape.roundRect.mWidth = width; + entry.shape.roundRect.mHeight = height; + entry.shape.roundRect.mRx = rx; + entry.shape.roundRect.mRy = ry; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Circles +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getCircle(float radius, SkPaint* paint) { + PathDescription entry(kShapeCircle, paint); + entry.shape.circle.mRadius = radius; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + path.addCircle(radius, radius, radius, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Ovals +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getOval(float width, float height, SkPaint* paint) { + PathDescription entry(kShapeOval, paint); + entry.shape.oval.mWidth = width; + entry.shape.oval.mHeight = height; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addOval(r, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Rects +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getRect(float width, float height, SkPaint* paint) { + PathDescription entry(kShapeRect, paint); + entry.shape.rect.mWidth = width; + entry.shape.rect.mHeight = height; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + path.addRect(r, SkPath::kCW_Direction); + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + +/////////////////////////////////////////////////////////////////////////////// +// Arcs +/////////////////////////////////////////////////////////////////////////////// + +PathTexture* PathCache::getArc(float width, float height, + float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { + PathDescription entry(kShapeArc, paint); + entry.shape.arc.mWidth = width; + entry.shape.arc.mHeight = height; + entry.shape.arc.mStartAngle = startAngle; + entry.shape.arc.mSweepAngle = sweepAngle; + entry.shape.arc.mUseCenter = useCenter; + + PathTexture* texture = get(entry); + + if (!texture) { + SkPath path; + SkRect r; + r.set(0.0f, 0.0f, width, height); + if (useCenter) { + path.moveTo(r.centerX(), r.centerY()); + } + path.arcTo(r, startAngle, sweepAngle, !useCenter); + if (useCenter) { + path.close(); + } + + texture = addTexture(entry, &path, paint); + } + + return texture; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 27031a5..e6d92df 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 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. @@ -17,17 +17,21 @@ #ifndef ANDROID_HWUI_PATH_CACHE_H #define ANDROID_HWUI_PATH_CACHE_H -#include <utils/Thread.h> +#include <GLES2/gl2.h> + +#include <utils/LruCache.h> +#include <utils/Mutex.h> #include <utils/Vector.h> #include "Debug.h" -#include "ShapeCache.h" -#include "thread/Signal.h" -#include "thread/Task.h" -#include "thread/TaskProcessor.h" +#include "Properties.h" +#include "Texture.h" +class SkBitmap; +class SkCanvas; class SkPaint; class SkPath; +class SkRect; namespace android { namespace uirenderer { @@ -35,56 +39,181 @@ namespace uirenderer { class Caches; /////////////////////////////////////////////////////////////////////////////// +// Defines +/////////////////////////////////////////////////////////////////////////////// + +// Debug +#if DEBUG_PATHS + #define PATH_LOGD(...) ALOGD(__VA_ARGS__) +#else + #define PATH_LOGD(...) +#endif + +/////////////////////////////////////////////////////////////////////////////// // Classes /////////////////////////////////////////////////////////////////////////////// -struct PathCacheEntry: public ShapeCacheEntry { - PathCacheEntry(SkPath* path, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapePath, paint) { - this->path = path; +/** + * Alpha texture used to represent a path. + */ +struct PathTexture: public Texture { + PathTexture(): Texture() { + } + + ~PathTexture() { + clearTask(); + } + + /** + * Left coordinate of the path bounds. + */ + float left; + /** + * Top coordinate of the path bounds. + */ + float top; + /** + * Offset to draw the path at the correct origin. + */ + float offset; + + sp<Task<SkBitmap*> > task() const { + return mTask; } - PathCacheEntry(): ShapeCacheEntry() { - path = NULL; + void setTask(const sp<Task<SkBitmap*> >& task) { + mTask = task; } - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(path)); - return JenkinsHashWhiten(hash); + void clearTask() { + if (mTask != NULL) { + mTask.clear(); + } } - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; +private: + sp<Task<SkBitmap*> > mTask; +}; // struct PathTexture + +enum ShapeType { + kShapeNone, + kShapeRect, + kShapeRoundRect, + kShapeCircle, + kShapeOval, + kShapeArc, + kShapePath +}; + +struct PathDescription { + ShapeType type; + SkPaint::Join join; + SkPaint::Cap cap; + SkPaint::Style style; + float miter; + float strokeWidth; + SkPathEffect* pathEffect; + union Shape { + struct Path { + SkPath* mPath; + } path; + struct RoundRect { + float mWidth; + float mHeight; + float mRx; + float mRy; + } roundRect; + struct Circle { + float mRadius; + } circle; + struct Oval { + float mWidth; + float mHeight; + } oval; + struct Rect { + float mWidth; + float mHeight; + } rect; + struct Arc { + float mWidth; + float mHeight; + float mStartAngle; + float mSweepAngle; + bool mUseCenter; + } arc; + } shape; + + PathDescription(); + PathDescription(ShapeType shapeType, SkPaint* paint); + + hash_t hash() const; - const PathCacheEntry& rhs = (const PathCacheEntry&) r; - return path - rhs.path; + int compare(const PathDescription& rhs) const; + + bool operator==(const PathDescription& other) const { + return compare(other) == 0; + } + + bool operator!=(const PathDescription& other) const { + return compare(other) != 0; } - SkPath* path; + friend inline int strictly_order_type( + const PathDescription& lhs, const PathDescription& rhs) { + return lhs.compare(rhs) < 0; + } -}; // PathCacheEntry + friend inline int compare_type(const PathDescription& lhs, const PathDescription& rhs) { + return lhs.compare(rhs); + } -inline hash_t hash_type(const PathCacheEntry& entry) { - return entry.hash(); -} + friend inline hash_t hash_type(const PathDescription& entry) { + return entry.hash(); + } +}; /** - * A simple LRU path cache. The cache has a maximum size expressed in bytes. + * A simple LRU shape 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 PathCache: public ShapeCache<PathCacheEntry> { +class PathCache: public OnEntryRemoved<PathDescription, PathTexture*> { public: PathCache(); ~PathCache(); /** - * Returns the texture associated with the specified path. If the texture - * cannot be found in the cache, a new texture is generated. + * Used as a callback when an entry is removed from the cache. + * Do not invoke directly. */ + void operator()(PathDescription& path, PathTexture*& texture); + + /** + * Clears the cache. This causes all textures to be deleted. + */ + void clear(); + + /** + * Sets the maximum size of the cache in bytes. + */ + void setMaxSize(uint32_t maxSize); + /** + * Returns the maximum size of the cache in bytes. + */ + uint32_t getMaxSize(); + /** + * Returns the current size of the cache in bytes. + */ + uint32_t getSize(); + + PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); + PathTexture* getCircle(float radius, SkPaint* paint); + PathTexture* getOval(float width, float height, SkPaint* paint); + PathTexture* getRect(float width, float height, SkPaint* paint); + PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, + bool useCenter, SkPaint* paint); PathTexture* get(SkPath* path, SkPaint* paint); + /** * Removes an entry. */ @@ -98,10 +227,63 @@ public: * Process deferred removals. */ void clearGarbage(); + /** + * Trims the contents of the cache, removing items until it's under its + * specified limit. + * + * Trimming is used for caches that support pre-caching from a worker + * thread. During pre-caching the maximum limit of the cache can be + * exceeded for the duration of the frame. It is therefore required to + * trim the cache at the end of the frame to keep the total amount of + * memory used under control. + */ + void trim(); + /** + * Precaches the specified path using background threads. + */ void precache(SkPath* path, SkPaint* paint); + static bool canDrawAsConvexPath(SkPath* path, SkPaint* paint); + static void computePathBounds(const SkPath* path, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height); + static void computeBounds(const SkRect& bounds, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height); + private: + PathTexture* addTexture(const PathDescription& entry, + const SkPath *path, const SkPaint* paint); + PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap); + void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture); + + PathTexture* get(const PathDescription& entry) { + return mCache.get(entry); + } + + /** + * Ensures there is enough space in the cache for a texture of the specified + * dimensions. + */ + void purgeCache(uint32_t width, uint32_t height); + + void removeTexture(PathTexture* texture); + + bool checkTextureSize(uint32_t width, uint32_t height) { + if (width > mMaxTextureSize || height > mMaxTextureSize) { + ALOGW("Shape too large to be rendered into a texture (%dx%d, max=%dx%d)", + width, height, mMaxTextureSize, mMaxTextureSize); + return false; + } + return true; + } + + /** + * Generates the texture from a bitmap into the specified texture structure. + */ + void generateTexture(SkBitmap& bitmap, Texture* texture); + + void init(); + class PathTask: public Task<SkBitmap*> { public: PathTask(SkPath* path, SkPaint* paint, PathTexture* texture): @@ -128,6 +310,13 @@ private: uint32_t mMaxTextureSize; }; + LruCache<PathDescription, PathTexture*> mCache; + uint32_t mSize; + uint32_t mMaxSize; + GLuint mMaxTextureSize; + + bool mDebugEnabled; + sp<PathProcessor> mProcessor; Vector<SkPath*> mGarbage; mutable Mutex mLock; diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index b8559bc..5f39abf 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -115,7 +115,6 @@ enum DebugLevel { #define PROPERTY_RENDER_BUFFER_CACHE_SIZE "ro.hwui.r_buffer_cache_size" #define PROPERTY_GRADIENT_CACHE_SIZE "ro.hwui.gradient_cache_size" #define PROPERTY_PATH_CACHE_SIZE "ro.hwui.path_cache_size" -#define PROPERTY_SHAPE_CACHE_SIZE "ro.hwui.shape_cache_size" #define PROPERTY_DROP_SHADOW_CACHE_SIZE "ro.hwui.drop_shadow_cache_size" #define PROPERTY_FBO_CACHE_SIZE "ro.hwui.fbo_cache_size" @@ -159,8 +158,7 @@ enum DebugLevel { #define DEFAULT_TEXTURE_CACHE_SIZE 24.0f #define DEFAULT_LAYER_CACHE_SIZE 16.0f #define DEFAULT_RENDER_BUFFER_CACHE_SIZE 2.0f -#define DEFAULT_PATH_CACHE_SIZE 4.0f -#define DEFAULT_SHAPE_CACHE_SIZE 1.0f +#define DEFAULT_PATH_CACHE_SIZE 10.0f #define DEFAULT_PATCH_CACHE_SIZE 512 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f #define DEFAULT_DROP_SHADOW_CACHE_SIZE 2.0f diff --git a/libs/hwui/ShapeCache.cpp b/libs/hwui/ShapeCache.cpp deleted file mode 100644 index 5a23235..0000000 --- a/libs/hwui/ShapeCache.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "OpenGLRenderer" - -#include "ShapeCache.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Rounded rects -/////////////////////////////////////////////////////////////////////////////// - -RoundRectShapeCache::RoundRectShapeCache(): ShapeCache<RoundRectShapeCacheEntry>( - "round rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { -} - -PathTexture* RoundRectShapeCache::getRoundRect(float width, float height, - float rx, float ry, SkPaint* paint) { - RoundRectShapeCacheEntry entry(width, height, rx, ry, paint); - PathTexture* texture = get(entry); - - if (!texture) { - SkPath path; - SkRect r; - r.set(0.0f, 0.0f, width, height); - path.addRoundRect(r, rx, ry, SkPath::kCW_Direction); - - texture = addTexture(entry, &path, paint); - } - - return texture; -} - -/////////////////////////////////////////////////////////////////////////////// -// Circles -/////////////////////////////////////////////////////////////////////////////// - -CircleShapeCache::CircleShapeCache(): ShapeCache<CircleShapeCacheEntry>( - "circle", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { -} - -PathTexture* CircleShapeCache::getCircle(float radius, SkPaint* paint) { - CircleShapeCacheEntry entry(radius, paint); - PathTexture* texture = get(entry); - - if (!texture) { - SkPath path; - path.addCircle(radius, radius, radius, SkPath::kCW_Direction); - - texture = addTexture(entry, &path, paint); - } - - return texture; -} - -/////////////////////////////////////////////////////////////////////////////// -// Ovals -/////////////////////////////////////////////////////////////////////////////// - -OvalShapeCache::OvalShapeCache(): ShapeCache<OvalShapeCacheEntry>( - "oval", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { -} - -PathTexture* OvalShapeCache::getOval(float width, float height, SkPaint* paint) { - OvalShapeCacheEntry entry(width, height, paint); - PathTexture* texture = get(entry); - - if (!texture) { - SkPath path; - SkRect r; - r.set(0.0f, 0.0f, width, height); - path.addOval(r, SkPath::kCW_Direction); - - texture = addTexture(entry, &path, paint); - } - - return texture; -} - -/////////////////////////////////////////////////////////////////////////////// -// Rects -/////////////////////////////////////////////////////////////////////////////// - -RectShapeCache::RectShapeCache(): ShapeCache<RectShapeCacheEntry>( - "rect", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { -} - -PathTexture* RectShapeCache::getRect(float width, float height, SkPaint* paint) { - RectShapeCacheEntry entry(width, height, paint); - PathTexture* texture = get(entry); - - if (!texture) { - SkRect bounds; - bounds.set(0.0f, 0.0f, width, height); - - float left, top, offset; - uint32_t rectWidth, rectHeight; - computeBounds(bounds, paint, left, top, offset, rectWidth, rectHeight); - - if (!checkTextureSize(rectWidth, rectHeight)) return NULL; - - purgeCache(rectWidth, rectHeight); - - SkBitmap bitmap; - initBitmap(bitmap, rectWidth, rectHeight); - - SkPaint pathPaint(*paint); - initPaint(pathPaint); - - SkCanvas canvas(bitmap); - canvas.translate(-left + offset, -top + offset); - canvas.drawRect(bounds, pathPaint); - - texture = createTexture(0, 0, offset, rectWidth, rectHeight, 0); - addTexture(entry, &bitmap, texture); - } - - return texture; -} - -/////////////////////////////////////////////////////////////////////////////// -// Arcs -/////////////////////////////////////////////////////////////////////////////// - -ArcShapeCache::ArcShapeCache(): ShapeCache<ArcShapeCacheEntry>( - "arc", PROPERTY_SHAPE_CACHE_SIZE, DEFAULT_SHAPE_CACHE_SIZE) { -} - -PathTexture* ArcShapeCache::getArc(float width, float height, - float startAngle, float sweepAngle, bool useCenter, SkPaint* paint) { - ArcShapeCacheEntry entry(width, height, startAngle, sweepAngle, useCenter, paint); - PathTexture* texture = get(entry); - - if (!texture) { - SkPath path; - SkRect r; - r.set(0.0f, 0.0f, width, height); - if (useCenter) { - path.moveTo(r.centerX(), r.centerY()); - } - path.arcTo(r, startAngle, sweepAngle, !useCenter); - if (useCenter) { - path.close(); - } - - texture = addTexture(entry, &path, paint); - } - - return texture; -} - -}; // namespace uirenderer -}; // namespace android diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h deleted file mode 100644 index 92314b0..0000000 --- a/libs/hwui/ShapeCache.h +++ /dev/null @@ -1,816 +0,0 @@ -/* - * Copyright (C) 2011 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 ANDROID_HWUI_SHAPE_CACHE_H -#define ANDROID_HWUI_SHAPE_CACHE_H - -#define ATRACE_TAG ATRACE_TAG_VIEW - -#include <GLES2/gl2.h> - -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkPaint.h> -#include <SkPath.h> -#include <SkRect.h> - -#include <utils/JenkinsHash.h> -#include <utils/LruCache.h> -#include <utils/Trace.h> - -#include "Debug.h" -#include "Properties.h" -#include "Texture.h" -#include "thread/Task.h" - -namespace android { -namespace uirenderer { - -/////////////////////////////////////////////////////////////////////////////// -// Defines -/////////////////////////////////////////////////////////////////////////////// - -// Debug -#if DEBUG_SHAPES - #define SHAPE_LOGD(...) ALOGD(__VA_ARGS__) -#else - #define SHAPE_LOGD(...) -#endif - -/////////////////////////////////////////////////////////////////////////////// -// Classes -/////////////////////////////////////////////////////////////////////////////// - -/** - * Alpha texture used to represent a path. - */ -struct PathTexture: public Texture { - PathTexture(): Texture() { - } - - ~PathTexture() { - clearTask(); - } - - /** - * Left coordinate of the path bounds. - */ - float left; - /** - * Top coordinate of the path bounds. - */ - float top; - /** - * Offset to draw the path at the correct origin. - */ - float offset; - - sp<Task<SkBitmap*> > task() const { - return mTask; - } - - void setTask(const sp<Task<SkBitmap*> >& task) { - mTask = task; - } - - void clearTask() { - if (mTask != NULL) { - mTask.clear(); - } - } - -private: - sp<Task<SkBitmap*> > mTask; -}; // struct PathTexture - -/** - * Describe a shape in the shape cache. - */ -struct ShapeCacheEntry { - enum ShapeType { - kShapeNone, - kShapeRect, - kShapeRoundRect, - kShapeCircle, - kShapeOval, - kShapeArc, - kShapePath - }; - - ShapeCacheEntry() { - shapeType = kShapeNone; - join = SkPaint::kDefault_Join; - cap = SkPaint::kDefault_Cap; - style = SkPaint::kFill_Style; - miter = 4.0f; - strokeWidth = 1.0f; - pathEffect = NULL; - } - - ShapeCacheEntry(ShapeType type, SkPaint* paint) { - shapeType = type; - join = paint->getStrokeJoin(); - cap = paint->getStrokeCap(); - miter = paint->getStrokeMiter(); - strokeWidth = paint->getStrokeWidth(); - style = paint->getStyle(); - pathEffect = paint->getPathEffect(); - } - - virtual ~ShapeCacheEntry() { - } - - virtual hash_t hash() const { - uint32_t hash = JenkinsHashMix(0, shapeType); - hash = JenkinsHashMix(hash, join); - hash = JenkinsHashMix(hash, cap); - hash = JenkinsHashMix(hash, style); - hash = JenkinsHashMix(hash, android::hash_type(miter)); - hash = JenkinsHashMix(hash, android::hash_type(strokeWidth)); - hash = JenkinsHashMix(hash, android::hash_type(pathEffect)); - return JenkinsHashWhiten(hash); - } - - virtual int compare(const ShapeCacheEntry& rhs) const { - int deltaInt = shapeType - rhs.shapeType; - if (deltaInt != 0) return deltaInt; - - deltaInt = join - rhs.join; - if (deltaInt != 0) return deltaInt; - - deltaInt = cap - rhs.cap; - if (deltaInt != 0) return deltaInt; - - deltaInt = style - rhs.style; - if (deltaInt != 0) return deltaInt; - - if (miter < rhs.miter) return -1; - if (miter > rhs.miter) return +1; - - if (strokeWidth < rhs.strokeWidth) return -1; - if (strokeWidth > rhs.strokeWidth) return +1; - - if (pathEffect < rhs.pathEffect) return -1; - if (pathEffect > rhs.pathEffect) return +1; - - return 0; - } - - bool operator==(const ShapeCacheEntry& other) const { - return compare(other) == 0; - } - - bool operator!=(const ShapeCacheEntry& other) const { - return compare(other) != 0; - } - - ShapeType shapeType; - SkPaint::Join join; - SkPaint::Cap cap; - SkPaint::Style style; - float miter; - float strokeWidth; - SkPathEffect* pathEffect; -}; // struct ShapeCacheEntry - -// Cache support - -inline int strictly_order_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) { - return lhs.compare(rhs) < 0; -} - -inline int compare_type(const ShapeCacheEntry& lhs, const ShapeCacheEntry& rhs) { - return lhs.compare(rhs); -} - -inline hash_t hash_type(const ShapeCacheEntry& entry) { - return entry.hash(); -} - -struct RoundRectShapeCacheEntry: public ShapeCacheEntry { - RoundRectShapeCacheEntry(float width, float height, float rx, float ry, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapeRoundRect, paint) { - mWidth = width; - mHeight = height; - mRx = rx; - mRy = ry; - } - - RoundRectShapeCacheEntry(): ShapeCacheEntry() { - mWidth = 0; - mHeight = 0; - mRx = 0; - mRy = 0; - } - - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(mWidth)); - hash = JenkinsHashMix(hash, android::hash_type(mHeight)); - hash = JenkinsHashMix(hash, android::hash_type(mRx)); - hash = JenkinsHashMix(hash, android::hash_type(mRy)); - return JenkinsHashWhiten(hash); - } - - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; - - const RoundRectShapeCacheEntry& rhs = (const RoundRectShapeCacheEntry&) r; - - if (mWidth < rhs.mWidth) return -1; - if (mWidth > rhs.mWidth) return +1; - - if (mHeight < rhs.mHeight) return -1; - if (mHeight > rhs.mHeight) return +1; - - if (mRx < rhs.mRx) return -1; - if (mRx > rhs.mRx) return +1; - - if (mRy < rhs.mRy) return -1; - if (mRy > rhs.mRy) return +1; - - return 0; - } - -private: - float mWidth; - float mHeight; - float mRx; - float mRy; -}; // RoundRectShapeCacheEntry - -inline hash_t hash_type(const RoundRectShapeCacheEntry& entry) { - return entry.hash(); -} - -struct CircleShapeCacheEntry: public ShapeCacheEntry { - CircleShapeCacheEntry(float radius, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapeCircle, paint) { - mRadius = radius; - } - - CircleShapeCacheEntry(): ShapeCacheEntry() { - mRadius = 0; - } - - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(mRadius)); - return JenkinsHashWhiten(hash); - } - - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; - - const CircleShapeCacheEntry& rhs = (const CircleShapeCacheEntry&) r; - - if (mRadius < rhs.mRadius) return -1; - if (mRadius > rhs.mRadius) return +1; - - return 0; - } - -private: - float mRadius; -}; // CircleShapeCacheEntry - -inline hash_t hash_type(const CircleShapeCacheEntry& entry) { - return entry.hash(); -} - -struct OvalShapeCacheEntry: public ShapeCacheEntry { - OvalShapeCacheEntry(float width, float height, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapeOval, paint) { - mWidth = width; - mHeight = height; - } - - OvalShapeCacheEntry(): ShapeCacheEntry() { - mWidth = mHeight = 0; - } - - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(mWidth)); - hash = JenkinsHashMix(hash, android::hash_type(mHeight)); - return JenkinsHashWhiten(hash); - } - - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; - - const OvalShapeCacheEntry& rhs = (const OvalShapeCacheEntry&) r; - - if (mWidth < rhs.mWidth) return -1; - if (mWidth > rhs.mWidth) return +1; - - if (mHeight < rhs.mHeight) return -1; - if (mHeight > rhs.mHeight) return +1; - - return 0; - } - -private: - float mWidth; - float mHeight; -}; // OvalShapeCacheEntry - -inline hash_t hash_type(const OvalShapeCacheEntry& entry) { - return entry.hash(); -} - -struct RectShapeCacheEntry: public ShapeCacheEntry { - RectShapeCacheEntry(float width, float height, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapeRect, paint) { - mWidth = width; - mHeight = height; - } - - RectShapeCacheEntry(): ShapeCacheEntry() { - mWidth = mHeight = 0; - } - - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(mWidth)); - hash = JenkinsHashMix(hash, android::hash_type(mHeight)); - return JenkinsHashWhiten(hash); - } - - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; - - const RectShapeCacheEntry& rhs = (const RectShapeCacheEntry&) r; - - if (mWidth < rhs.mWidth) return -1; - if (mWidth > rhs.mWidth) return +1; - - if (mHeight < rhs.mHeight) return -1; - if (mHeight > rhs.mHeight) return +1; - - return 0; - } - -private: - float mWidth; - float mHeight; -}; // RectShapeCacheEntry - -inline hash_t hash_type(const RectShapeCacheEntry& entry) { - return entry.hash(); -} - -struct ArcShapeCacheEntry: public ShapeCacheEntry { - ArcShapeCacheEntry(float width, float height, float startAngle, float sweepAngle, - bool useCenter, SkPaint* paint): - ShapeCacheEntry(ShapeCacheEntry::kShapeArc, paint) { - mWidth = width; - mHeight = height; - mStartAngle = startAngle; - mSweepAngle = sweepAngle; - mUseCenter = useCenter ? 1 : 0; - } - - ArcShapeCacheEntry(): ShapeCacheEntry() { - mWidth = 0; - mHeight = 0; - mStartAngle = 0; - mSweepAngle = 0; - mUseCenter = 0; - } - - hash_t hash() const { - uint32_t hash = ShapeCacheEntry::hash(); - hash = JenkinsHashMix(hash, android::hash_type(mWidth)); - hash = JenkinsHashMix(hash, android::hash_type(mHeight)); - hash = JenkinsHashMix(hash, android::hash_type(mStartAngle)); - hash = JenkinsHashMix(hash, android::hash_type(mSweepAngle)); - hash = JenkinsHashMix(hash, mUseCenter); - return JenkinsHashWhiten(hash); - } - - int compare(const ShapeCacheEntry& r) const { - int deltaInt = ShapeCacheEntry::compare(r); - if (deltaInt != 0) return deltaInt; - - const ArcShapeCacheEntry& rhs = (const ArcShapeCacheEntry&) r; - - if (mWidth < rhs.mWidth) return -1; - if (mWidth > rhs.mWidth) return +1; - - if (mHeight < rhs.mHeight) return -1; - if (mHeight > rhs.mHeight) return +1; - - if (mStartAngle < rhs.mStartAngle) return -1; - if (mStartAngle > rhs.mStartAngle) return +1; - - if (mSweepAngle < rhs.mSweepAngle) return -1; - if (mSweepAngle > rhs.mSweepAngle) return +1; - - return mUseCenter - rhs.mUseCenter; - } - -private: - float mWidth; - float mHeight; - float mStartAngle; - float mSweepAngle; - uint32_t mUseCenter; -}; // ArcShapeCacheEntry - -inline hash_t hash_type(const ArcShapeCacheEntry& entry) { - return entry.hash(); -} - -/** - * A simple LRU shape 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. - */ -template<typename Entry> -class ShapeCache: public OnEntryRemoved<Entry, PathTexture*> { -public: - ShapeCache(const char* name, const char* propertyName, float defaultSize); - ~ShapeCache(); - - /** - * Used as a callback when an entry is removed from the cache. - * Do not invoke directly. - */ - void operator()(Entry& path, PathTexture*& texture); - - /** - * Clears the cache. This causes all textures to be deleted. - */ - void clear(); - - /** - * Sets the maximum size of the cache in bytes. - */ - void setMaxSize(uint32_t maxSize); - /** - * Returns the maximum size of the cache in bytes. - */ - uint32_t getMaxSize(); - /** - * Returns the current size of the cache in bytes. - */ - uint32_t getSize(); - - /** - * Trims the contents of the cache, removing items until it's under its - * specified limit. - * - * Trimming is used for caches that support pre-caching from a worker - * thread. During pre-caching the maximum limit of the cache can be - * exceeded for the duration of the frame. It is therefore required to - * trim the cache at the end of the frame to keep the total amount of - * memory used under control. - * - * Only the PathCache currently supports pre-caching. - */ - void trim(); - - static void computePathBounds(const SkPath* path, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { - const SkRect& bounds = path->getBounds(); - computeBounds(bounds, paint, left, top, offset, width, height); - } - - static void computeBounds(const SkRect& bounds, const SkPaint* paint, - float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { - const float pathWidth = fmax(bounds.width(), 1.0f); - const float pathHeight = fmax(bounds.height(), 1.0f); - - left = bounds.fLeft; - top = bounds.fTop; - - offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); - - width = uint32_t(pathWidth + offset * 2.0 + 0.5); - height = uint32_t(pathHeight + offset * 2.0 + 0.5); - } - - static void drawPath(const SkPath *path, const SkPaint* paint, SkBitmap& bitmap, - float left, float top, float offset, uint32_t width, uint32_t height) { - initBitmap(bitmap, width, height); - - SkPaint pathPaint(*paint); - initPaint(pathPaint); - - SkCanvas canvas(bitmap); - canvas.translate(-left + offset, -top + offset); - canvas.drawPath(*path, pathPaint); - } - -protected: - PathTexture* addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint); - PathTexture* addTexture(const Entry& entry, SkBitmap* bitmap); - void addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture); - - /** - * Ensures there is enough space in the cache for a texture of the specified - * dimensions. - */ - void purgeCache(uint32_t width, uint32_t height); - - PathTexture* get(Entry entry) { - return mCache.get(entry); - } - - void removeTexture(PathTexture* texture); - - bool checkTextureSize(uint32_t width, uint32_t height) { - if (width > mMaxTextureSize || height > mMaxTextureSize) { - ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)", - mName, width, height, mMaxTextureSize, mMaxTextureSize); - return false; - } - return true; - } - - static PathTexture* createTexture(float left, float top, float offset, - uint32_t width, uint32_t height, uint32_t id) { - PathTexture* texture = new PathTexture(); - texture->left = left; - texture->top = top; - texture->offset = offset; - texture->width = width; - texture->height = height; - texture->generation = id; - return texture; - } - - static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) { - bitmap.setConfig(SkBitmap::kA8_Config, width, height); - bitmap.allocPixels(); - bitmap.eraseColor(0); - } - - static void initPaint(SkPaint& paint) { - // Make sure the paint is opaque, color, alpha, filter, etc. - // will be applied later when compositing the alpha8 texture - paint.setColor(0xff000000); - paint.setAlpha(255); - paint.setColorFilter(NULL); - paint.setMaskFilter(NULL); - paint.setShader(NULL); - SkXfermode* mode = SkXfermode::Create(SkXfermode::kSrc_Mode); - SkSafeUnref(paint.setXfermode(mode)); - } - - LruCache<Entry, PathTexture*> mCache; - uint32_t mSize; - uint32_t mMaxSize; - GLuint mMaxTextureSize; - - char* mName; - bool mDebugEnabled; - -private: - /** - * Generates the texture from a bitmap into the specified texture structure. - */ - void generateTexture(SkBitmap& bitmap, Texture* texture); - - void init(); -}; // class ShapeCache - -class RoundRectShapeCache: public ShapeCache<RoundRectShapeCacheEntry> { -public: - RoundRectShapeCache(); - - PathTexture* getRoundRect(float width, float height, float rx, float ry, SkPaint* paint); -}; // class RoundRectShapeCache - -class CircleShapeCache: public ShapeCache<CircleShapeCacheEntry> { -public: - CircleShapeCache(); - - PathTexture* getCircle(float radius, SkPaint* paint); -}; // class CircleShapeCache - -class OvalShapeCache: public ShapeCache<OvalShapeCacheEntry> { -public: - OvalShapeCache(); - - PathTexture* getOval(float width, float height, SkPaint* paint); -}; // class OvalShapeCache - -class RectShapeCache: public ShapeCache<RectShapeCacheEntry> { -public: - RectShapeCache(); - - PathTexture* getRect(float width, float height, SkPaint* paint); -}; // class RectShapeCache - -class ArcShapeCache: public ShapeCache<ArcShapeCacheEntry> { -public: - ArcShapeCache(); - - PathTexture* getArc(float width, float height, float startAngle, float sweepAngle, - bool useCenter, SkPaint* paint); -}; // class ArcShapeCache - -/////////////////////////////////////////////////////////////////////////////// -// Constructors/destructor -/////////////////////////////////////////////////////////////////////////////// - -template<class Entry> -ShapeCache<Entry>::ShapeCache(const char* name, const char* propertyName, float defaultSize): - mCache(LruCache<ShapeCacheEntry, PathTexture*>::kUnlimitedCapacity), - mSize(0), mMaxSize(MB(defaultSize)) { - char property[PROPERTY_VALUE_MAX]; - if (property_get(propertyName, property, NULL) > 0) { - INIT_LOGD(" Setting %s cache size to %sMB", name, property); - setMaxSize(MB(atof(property))); - } else { - INIT_LOGD(" Using default %s cache size of %.2fMB", name, defaultSize); - } - - size_t len = strlen(name); - mName = new char[len + 1]; - strcpy(mName, name); - mName[len] = '\0'; - - init(); -} - -template<class Entry> -ShapeCache<Entry>::~ShapeCache() { - mCache.clear(); - delete[] mName; -} - -template<class Entry> -void ShapeCache<Entry>::init() { - mCache.setOnEntryRemovedListener(this); - - GLint maxTextureSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); - mMaxTextureSize = maxTextureSize; - - mDebugEnabled = readDebugLevel() & kDebugCaches; -} - -/////////////////////////////////////////////////////////////////////////////// -// Size management -/////////////////////////////////////////////////////////////////////////////// - -template<class Entry> -uint32_t ShapeCache<Entry>::getSize() { - return mSize; -} - -template<class Entry> -uint32_t ShapeCache<Entry>::getMaxSize() { - return mMaxSize; -} - -template<class Entry> -void ShapeCache<Entry>::setMaxSize(uint32_t maxSize) { - mMaxSize = maxSize; - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - -/////////////////////////////////////////////////////////////////////////////// -// Callbacks -/////////////////////////////////////////////////////////////////////////////// - -template<class Entry> -void ShapeCache<Entry>::operator()(Entry& path, PathTexture*& texture) { - removeTexture(texture); -} - -/////////////////////////////////////////////////////////////////////////////// -// Caching -/////////////////////////////////////////////////////////////////////////////// - -template<class Entry> -void ShapeCache<Entry>::removeTexture(PathTexture* texture) { - if (texture) { - const uint32_t size = texture->width * texture->height; - mSize -= size; - - SHAPE_LOGD("ShapeCache::callback: delete %s: name, size, mSize = %d, %d, %d", - mName, texture->id, size, mSize); - if (mDebugEnabled) { - ALOGD("Shape %s deleted, size = %d", mName, size); - } - - if (texture->id) { - glDeleteTextures(1, &texture->id); - } - delete texture; - } -} - -template<class Entry> -void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) { - const uint32_t size = width * height; - // Don't even try to cache a bitmap that's bigger than the cache - if (size < mMaxSize) { - while (mSize + size > mMaxSize) { - mCache.removeOldest(); - } - } -} - -template<class Entry> -void ShapeCache<Entry>::trim() { - while (mSize > mMaxSize) { - mCache.removeOldest(); - } -} - -template<class Entry> -PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path, - const SkPaint* paint) { - ATRACE_CALL(); - - float left, top, offset; - uint32_t width, height; - computePathBounds(path, paint, left, top, offset, width, height); - - if (!checkTextureSize(width, height)) return NULL; - - purgeCache(width, height); - - SkBitmap bitmap; - drawPath(path, paint, bitmap, left, top, offset, width, height); - - PathTexture* texture = createTexture(left, top, offset, width, height, - path->getGenerationID()); - addTexture(entry, &bitmap, texture); - - return texture; -} - -template<class Entry> -void ShapeCache<Entry>::addTexture(const Entry& entry, SkBitmap* bitmap, PathTexture* texture) { - generateTexture(*bitmap, texture); - - uint32_t size = texture->width * texture->height; - if (size < mMaxSize) { - mSize += size; - SHAPE_LOGD("ShapeCache::get: create %s: name, size, mSize = %d, %d, %d", - mName, texture->id, size, mSize); - if (mDebugEnabled) { - ALOGD("Shape %s created, size = %d", mName, size); - } - mCache.put(entry, texture); - } else { - texture->cleanup = true; - } -} - -template<class Entry> -void ShapeCache<Entry>::clear() { - mCache.clear(); -} - -template<class Entry> -void ShapeCache<Entry>::generateTexture(SkBitmap& bitmap, Texture* texture) { - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - ALOGE("Cannot generate texture from bitmap"); - return; - } - - glGenTextures(1, &texture->id); - - glBindTexture(GL_TEXTURE_2D, texture->id); - // Textures are Alpha8 - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - - texture->blend = true; - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture->width, texture->height, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.getPixels()); - - texture->setFilter(GL_LINEAR); - texture->setWrap(GL_CLAMP_TO_EDGE); -} - -}; // namespace uirenderer -}; // namespace android - -#endif // ANDROID_HWUI_SHAPE_CACHE_H |