Precache paths from a worker thread
Change-Id: I3e7b53d67e0e03e403beaf55c39350ead7f1e309
This commit is contained in:
@@ -1005,7 +1005,7 @@ public:
|
||||
: DrawBoundedOp(paint), mPath(path) {
|
||||
float left, top, offset;
|
||||
uint32_t width, height;
|
||||
computePathBounds(path, paint, left, top, offset, width, height);
|
||||
PathCache::computePathBounds(path, paint, left, top, offset, width, height);
|
||||
left -= offset;
|
||||
top -= offset;
|
||||
mLocalBounds.set(left, top, left + width, top + height);
|
||||
@@ -1016,6 +1016,11 @@ public:
|
||||
return renderer.drawPath(mPath, getPaint(renderer));
|
||||
}
|
||||
|
||||
virtual void onDrawOpDeferred(OpenGLRenderer& renderer) {
|
||||
SkPaint* paint = getPaint(renderer);
|
||||
renderer.getCaches().pathCache.precache(mPath, paint);
|
||||
}
|
||||
|
||||
virtual void output(int level, uint32_t flags) {
|
||||
OP_LOG("Draw Path %p in "RECT_STRING, mPath, RECT_ARGS(mLocalBounds));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
|
||||
#include <utils/JenkinsHash.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
#include "Caches.h"
|
||||
#include "Debug.h"
|
||||
|
||||
@@ -276,6 +276,12 @@ void OpenGLRenderer::finish() {
|
||||
renderOverdraw();
|
||||
endTiling();
|
||||
|
||||
// When finish() is invoked on FBO 0 we've reached the end
|
||||
// of the current frame
|
||||
if (getTargetFbo() == 0) {
|
||||
mCaches.pathCache.trim();
|
||||
}
|
||||
|
||||
if (!suppressErrorChecks()) {
|
||||
#if DEBUG_OPENGL
|
||||
GLenum status = GL_NO_ERROR;
|
||||
|
||||
@@ -16,34 +16,84 @@
|
||||
|
||||
#define LOG_TAG "OpenGLRenderer"
|
||||
|
||||
#include <utils/threads.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
#include "Caches.h"
|
||||
#include "PathCache.h"
|
||||
#include "Properties.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
// Defined in ShapeCache.h
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Path precaching
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
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);
|
||||
bool PathCache::PrecacheThread::threadLoop() {
|
||||
mSignal.wait();
|
||||
Vector<Task> tasks;
|
||||
{
|
||||
Mutex::Autolock l(mLock);
|
||||
tasks = mTasks;
|
||||
mTasks.clear();
|
||||
}
|
||||
|
||||
Caches& caches = Caches::getInstance();
|
||||
uint32_t maxSize = caches.maxTextureSize;
|
||||
|
||||
ATRACE_BEGIN("pathPrecache");
|
||||
for (size_t i = 0; i < tasks.size(); i++) {
|
||||
const Task& task = tasks.itemAt(i);
|
||||
|
||||
float left, top, offset;
|
||||
uint32_t width, height;
|
||||
PathCache::computePathBounds(task.path, task.paint, left, top, offset, width, height);
|
||||
|
||||
if (width <= maxSize && height <= maxSize) {
|
||||
SkBitmap* bitmap = new SkBitmap();
|
||||
|
||||
PathTexture* texture = task.texture;
|
||||
texture->left = left;
|
||||
texture->top = top;
|
||||
texture->offset = offset;
|
||||
texture->width = width;
|
||||
texture->height = height;
|
||||
|
||||
PathCache::drawPath(task.path, task.paint, *bitmap, left, top, offset, width, height);
|
||||
|
||||
texture->future()->produce(bitmap);
|
||||
} else {
|
||||
task.texture->future()->produce(NULL);
|
||||
}
|
||||
}
|
||||
ATRACE_END();
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
void PathCache::PrecacheThread::addTask(PathTexture* texture, SkPath* path, SkPaint* paint) {
|
||||
if (!isRunning()) {
|
||||
run("libhwui:pathPrecache", PRIORITY_DEFAULT);
|
||||
}
|
||||
|
||||
left = bounds.fLeft;
|
||||
top = bounds.fTop;
|
||||
Task task;
|
||||
task.texture = texture;
|
||||
task.path = path;
|
||||
task.paint = paint;
|
||||
|
||||
offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
|
||||
Mutex::Autolock l(mLock);
|
||||
mTasks.add(task);
|
||||
mSignal.signal();
|
||||
}
|
||||
|
||||
width = uint32_t(pathWidth + offset * 2.0 + 0.5);
|
||||
height = uint32_t(pathHeight + offset * 2.0 + 0.5);
|
||||
void PathCache::PrecacheThread::exit() {
|
||||
{
|
||||
Mutex::Autolock l(mLock);
|
||||
mTasks.clear();
|
||||
}
|
||||
requestExit();
|
||||
mSignal.signal();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -51,7 +101,11 @@ void computeBounds(const SkRect& bounds, const SkPaint* paint,
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PathCache::PathCache(): ShapeCache<PathCacheEntry>("path",
|
||||
PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) {
|
||||
PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE), mThread(new PrecacheThread()) {
|
||||
}
|
||||
|
||||
PathCache::~PathCache() {
|
||||
mThread->exit();
|
||||
}
|
||||
|
||||
void PathCache::remove(SkPath* path) {
|
||||
@@ -60,7 +114,7 @@ void PathCache::remove(SkPath* path) {
|
||||
|
||||
while (i.next()) {
|
||||
const PathCacheEntry& key = i.key();
|
||||
if (key.path == path) {
|
||||
if (key.path == path || key.path == path->getSourcePath()) {
|
||||
pathsToRemove.push(key);
|
||||
}
|
||||
}
|
||||
@@ -71,12 +125,12 @@ void PathCache::remove(SkPath* path) {
|
||||
}
|
||||
|
||||
void PathCache::removeDeferred(SkPath* path) {
|
||||
Mutex::Autolock _l(mLock);
|
||||
Mutex::Autolock l(mLock);
|
||||
mGarbage.push(path);
|
||||
}
|
||||
|
||||
void PathCache::clearGarbage() {
|
||||
Mutex::Autolock _l(mLock);
|
||||
Mutex::Autolock l(mLock);
|
||||
size_t count = mGarbage.size();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
remove(mGarbage.itemAt(i));
|
||||
@@ -84,24 +138,86 @@ void PathCache::clearGarbage() {
|
||||
mGarbage.clear();
|
||||
}
|
||||
|
||||
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
|
||||
/**
|
||||
* To properly handle path mutations at draw time we always make a copy
|
||||
* of paths objects when recording display lists. The source path points
|
||||
* to the path we originally copied the path from. This ensures we use
|
||||
* the original path as a cache key the first time a path is inserted
|
||||
* in the cache. The source path is also used to reclaim garbage when a
|
||||
* Dalvik Path object is collected.
|
||||
*/
|
||||
static SkPath* getSourcePath(SkPath* path) {
|
||||
const SkPath* sourcePath = path->getSourcePath();
|
||||
if (sourcePath && sourcePath->getGenerationID() == path->getGenerationID()) {
|
||||
path = const_cast<SkPath*>(sourcePath);
|
||||
return const_cast<SkPath*>(sourcePath);
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
PathTexture* PathCache::get(SkPath* path, SkPaint* paint) {
|
||||
path = getSourcePath(path);
|
||||
|
||||
PathCacheEntry entry(path, paint);
|
||||
PathTexture* texture = mCache.get(entry);
|
||||
|
||||
if (!texture) {
|
||||
texture = addTexture(entry, path, paint);
|
||||
} else if (path->getGenerationID() != texture->generation) {
|
||||
mCache.remove(entry);
|
||||
texture = addTexture(entry, path, paint);
|
||||
} else {
|
||||
// A bitmap is attached to the texture, this means we need to
|
||||
// upload it as a GL texture
|
||||
if (texture->future() != NULL) {
|
||||
// But we must first wait for the worker thread to be done
|
||||
// producing the bitmap, so let's wait
|
||||
SkBitmap* bitmap = texture->future()->get();
|
||||
if (bitmap) {
|
||||
addTexture(entry, bitmap, texture);
|
||||
texture->clearFuture();
|
||||
} else {
|
||||
ALOGW("Path too large to be rendered into a texture (%dx%d)",
|
||||
texture->width, texture->height);
|
||||
texture->clearFuture();
|
||||
texture = NULL;
|
||||
mCache.remove(entry);
|
||||
}
|
||||
} else if (path->getGenerationID() != texture->generation) {
|
||||
mCache.remove(entry);
|
||||
texture = addTexture(entry, path, paint);
|
||||
}
|
||||
}
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
void PathCache::precache(SkPath* path, SkPaint* paint) {
|
||||
path = getSourcePath(path);
|
||||
|
||||
PathCacheEntry entry(path, paint);
|
||||
PathTexture* texture = mCache.get(entry);
|
||||
|
||||
bool generate = false;
|
||||
if (!texture) {
|
||||
generate = true;
|
||||
} else if (path->getGenerationID() != texture->generation) {
|
||||
mCache.remove(entry);
|
||||
generate = true;
|
||||
}
|
||||
|
||||
if (generate) {
|
||||
// It is important to specify the generation ID so we do not
|
||||
// attempt to precache the same path several times
|
||||
texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID(), true);
|
||||
|
||||
// During the precaching phase we insert path texture objects into
|
||||
// the cache that do not point to any GL texture. They are instead
|
||||
// treated as a task for the precaching worker thread. This is why
|
||||
// we do not check the cache limit when inserting these objects.
|
||||
// The conversion into GL texture will happen in get(), when a client
|
||||
// asks for a path texture. This is also when the cache limit will
|
||||
// be enforced.
|
||||
mCache.put(entry, texture);
|
||||
mThread->addTask(texture, path, paint);
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
@@ -17,14 +17,21 @@
|
||||
#ifndef ANDROID_HWUI_PATH_CACHE_H
|
||||
#define ANDROID_HWUI_PATH_CACHE_H
|
||||
|
||||
#include <utils/Thread.h>
|
||||
#include <utils/Vector.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "ShapeCache.h"
|
||||
#include "thread/Signal.h"
|
||||
|
||||
class SkPaint;
|
||||
class SkPath;
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class Caches;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Classes
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -69,6 +76,7 @@ inline hash_t hash_type(const PathCacheEntry& entry) {
|
||||
class PathCache: public ShapeCache<PathCacheEntry> {
|
||||
public:
|
||||
PathCache();
|
||||
~PathCache();
|
||||
|
||||
/**
|
||||
* Returns the texture associated with the specified path. If the texture
|
||||
@@ -89,7 +97,35 @@ public:
|
||||
*/
|
||||
void clearGarbage();
|
||||
|
||||
void precache(SkPath* path, SkPaint* paint);
|
||||
|
||||
private:
|
||||
class PrecacheThread: public Thread {
|
||||
public:
|
||||
PrecacheThread(): mSignal(Condition::WAKE_UP_ONE) { }
|
||||
|
||||
void addTask(PathTexture* texture, SkPath* path, SkPaint* paint);
|
||||
void exit();
|
||||
|
||||
private:
|
||||
struct Task {
|
||||
PathTexture* texture;
|
||||
SkPath* path;
|
||||
SkPaint* paint;
|
||||
};
|
||||
|
||||
virtual bool threadLoop();
|
||||
|
||||
// Lock for the list of tasks
|
||||
Mutex mLock;
|
||||
Vector<Task> mTasks;
|
||||
|
||||
// Signal used to wake up the thread when a new
|
||||
// task is available in the list
|
||||
mutable Signal mSignal;
|
||||
};
|
||||
|
||||
sp<PrecacheThread> mThread;
|
||||
Vector<SkPath*> mGarbage;
|
||||
mutable Mutex mLock;
|
||||
}; // class PathCache
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#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>
|
||||
@@ -27,10 +29,13 @@
|
||||
|
||||
#include <utils/JenkinsHash.h>
|
||||
#include <utils/LruCache.h>
|
||||
#include <utils/Trace.h>
|
||||
#include <utils/CallStack.h>
|
||||
|
||||
#include "Debug.h"
|
||||
#include "Properties.h"
|
||||
#include "Texture.h"
|
||||
#include "thread/Future.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
@@ -57,6 +62,16 @@ struct PathTexture: public Texture {
|
||||
PathTexture(): Texture() {
|
||||
}
|
||||
|
||||
PathTexture(bool hasFuture): Texture() {
|
||||
if (hasFuture) {
|
||||
mFuture = new Future<SkBitmap*>();
|
||||
}
|
||||
}
|
||||
|
||||
~PathTexture() {
|
||||
clearFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
* Left coordinate of the path bounds.
|
||||
*/
|
||||
@@ -69,6 +84,20 @@ struct PathTexture: public Texture {
|
||||
* Offset to draw the path at the correct origin.
|
||||
*/
|
||||
float offset;
|
||||
|
||||
sp<Future<SkBitmap*> > future() const {
|
||||
return mFuture;
|
||||
}
|
||||
|
||||
void clearFuture() {
|
||||
if (mFuture != NULL) {
|
||||
delete mFuture->get();
|
||||
mFuture.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
sp<Future<SkBitmap*> > mFuture;
|
||||
}; // struct PathTexture
|
||||
|
||||
/**
|
||||
@@ -449,6 +478,52 @@ public:
|
||||
*/
|
||||
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);
|
||||
@@ -460,17 +535,51 @@ protected:
|
||||
*/
|
||||
void purgeCache(uint32_t width, uint32_t height);
|
||||
|
||||
void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height);
|
||||
void initPaint(SkPaint& paint);
|
||||
|
||||
bool checkTextureSize(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, bool hasFuture = false) {
|
||||
PathTexture* texture = new PathTexture(hasFuture);
|
||||
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;
|
||||
@@ -617,23 +726,6 @@ void ShapeCache<Entry>::removeTexture(PathTexture* texture) {
|
||||
}
|
||||
}
|
||||
|
||||
void computePathBounds(const SkPath* path, const SkPaint* paint,
|
||||
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
|
||||
void computeBounds(const SkRect& bounds, const SkPaint* paint,
|
||||
float& left, float& top, float& offset, uint32_t& width, uint32_t& height);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
template<class Entry>
|
||||
void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
|
||||
const uint32_t size = width * height;
|
||||
@@ -646,38 +738,16 @@ void ShapeCache<Entry>::purgeCache(uint32_t width, uint32_t height) {
|
||||
}
|
||||
|
||||
template<class Entry>
|
||||
void ShapeCache<Entry>::initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
|
||||
bitmap.setConfig(SkBitmap::kA8_Config, width, height);
|
||||
bitmap.allocPixels();
|
||||
bitmap.eraseColor(0);
|
||||
}
|
||||
|
||||
template<class Entry>
|
||||
void ShapeCache<Entry>::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));
|
||||
}
|
||||
|
||||
template<class Entry>
|
||||
bool ShapeCache<Entry>::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;
|
||||
void ShapeCache<Entry>::trim() {
|
||||
while (mSize > mMaxSize) {
|
||||
mCache.removeOldest();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -688,16 +758,10 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat
|
||||
purgeCache(width, height);
|
||||
|
||||
SkBitmap bitmap;
|
||||
initBitmap(bitmap, width, height);
|
||||
drawPath(path, paint, bitmap, left, top, offset, width, height);
|
||||
|
||||
SkPaint pathPaint(*paint);
|
||||
initPaint(pathPaint);
|
||||
|
||||
SkCanvas canvas(bitmap);
|
||||
canvas.translate(-left + offset, -top + offset);
|
||||
canvas.drawPath(*path, pathPaint);
|
||||
|
||||
PathTexture* texture = createTexture(left, top, offset, width, height, path->getGenerationID());
|
||||
PathTexture* texture = createTexture(left, top, offset, width, height,
|
||||
path->getGenerationID());
|
||||
addTexture(entry, &bitmap, texture);
|
||||
|
||||
return texture;
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <SkCanvas.h>
|
||||
|
||||
#include <utils/threads.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include "Caches.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
58
libs/hwui/thread/Barrier.h
Normal file
58
libs/hwui/thread/Barrier.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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_BARRIER_H
|
||||
#define ANDROID_HWUI_BARRIER_H
|
||||
|
||||
#include <utils/Condition.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class Barrier {
|
||||
public:
|
||||
Barrier(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mOpened(false) { }
|
||||
~Barrier() { }
|
||||
|
||||
void open() {
|
||||
Mutex::Autolock l(mLock);
|
||||
mOpened = true;
|
||||
mCondition.signal(mType);
|
||||
}
|
||||
|
||||
void close() {
|
||||
Mutex::Autolock l(mLock);
|
||||
mOpened = false;
|
||||
}
|
||||
|
||||
void wait() const {
|
||||
Mutex::Autolock l(mLock);
|
||||
while (!mOpened) {
|
||||
mCondition.wait(mLock);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Condition::WakeUpType mType;
|
||||
volatile bool mOpened;
|
||||
mutable Mutex mLock;
|
||||
mutable Condition mCondition;
|
||||
};
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_HWUI_BARRIER_H
|
||||
58
libs/hwui/thread/Future.h
Normal file
58
libs/hwui/thread/Future.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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_FUTURE_H
|
||||
#define ANDROID_HWUI_FUTURE_H
|
||||
|
||||
#include <utils/RefBase.h>
|
||||
|
||||
#include "Barrier.h"
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
template<class T>
|
||||
class Future: public LightRefBase<Future<T> > {
|
||||
public:
|
||||
Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE): mBarrier(type), mResult() { }
|
||||
~Future() { }
|
||||
|
||||
/**
|
||||
* Returns the result of this future, blocking if
|
||||
* the result is not available yet.
|
||||
*/
|
||||
T get() const {
|
||||
mBarrier.wait();
|
||||
return mResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method must be called only once.
|
||||
*/
|
||||
void produce(T result) {
|
||||
mResult = result;
|
||||
mBarrier.open();
|
||||
}
|
||||
|
||||
private:
|
||||
Barrier mBarrier;
|
||||
T mResult;
|
||||
};
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_HWUI_FUTURE_H
|
||||
56
libs/hwui/thread/Signal.h
Normal file
56
libs/hwui/thread/Signal.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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_SIGNAL_H
|
||||
#define ANDROID_HWUI_SIGNAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
namespace android {
|
||||
namespace uirenderer {
|
||||
|
||||
class Signal {
|
||||
public:
|
||||
Signal(Condition::WakeUpType type = Condition::WAKE_UP_ALL) : mType(type), mSignaled(false) { }
|
||||
~Signal() { }
|
||||
|
||||
void signal() {
|
||||
Mutex::Autolock l(mLock);
|
||||
mSignaled = true;
|
||||
mCondition.signal(mType);
|
||||
}
|
||||
|
||||
void wait() {
|
||||
Mutex::Autolock l(mLock);
|
||||
while (!mSignaled) {
|
||||
mCondition.wait(mLock);
|
||||
}
|
||||
mSignaled = false;
|
||||
}
|
||||
|
||||
private:
|
||||
Condition::WakeUpType mType;
|
||||
volatile bool mSignaled;
|
||||
mutable Mutex mLock;
|
||||
mutable Condition mCondition;
|
||||
};
|
||||
|
||||
}; // namespace uirenderer
|
||||
}; // namespace android
|
||||
|
||||
#endif // ANDROID_HWUI_SIGNAL_H
|
||||
@@ -45,13 +45,13 @@ public class PathsCacheActivity extends Activity {
|
||||
setContentView(view);
|
||||
}
|
||||
|
||||
private Path makePath() {
|
||||
private static Path makePath() {
|
||||
Path path = new Path();
|
||||
buildPath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
private void buildPath(Path path) {
|
||||
private static void buildPath(Path path) {
|
||||
path.moveTo(0.0f, 0.0f);
|
||||
path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f);
|
||||
path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f);
|
||||
|
||||
Reference in New Issue
Block a user