diff options
Diffstat (limited to 'libs/hwui')
-rw-r--r-- | libs/hwui/Android.mk | 1 | ||||
-rw-r--r-- | libs/hwui/Caches.h | 5 | ||||
-rw-r--r-- | libs/hwui/PathCache.cpp | 109 | ||||
-rw-r--r-- | libs/hwui/PathCache.h | 39 | ||||
-rw-r--r-- | libs/hwui/ShapeCache.h | 32 | ||||
-rw-r--r-- | libs/hwui/thread/Future.h | 2 | ||||
-rw-r--r-- | libs/hwui/thread/Task.h | 63 | ||||
-rw-r--r-- | libs/hwui/thread/TaskManager.cpp | 118 | ||||
-rw-r--r-- | libs/hwui/thread/TaskManager.h | 100 | ||||
-rw-r--r-- | libs/hwui/thread/TaskProcessor.h | 72 |
10 files changed, 436 insertions, 105 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index 85b2052..1618110 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -7,6 +7,7 @@ ifeq ($(USE_OPENGL_RENDERER),true) LOCAL_SRC_FILES:= \ utils/Blur.cpp \ utils/SortedListImpl.cpp \ + thread/TaskManager.cpp \ font/CacheTexture.cpp \ font/Font.cpp \ FontRenderer.cpp \ diff --git a/libs/hwui/Caches.h b/libs/hwui/Caches.h index ca699d5..dc32a7e 100644 --- a/libs/hwui/Caches.h +++ b/libs/hwui/Caches.h @@ -25,6 +25,9 @@ #include <cutils/compiler.h> +#include "thread/TaskProcessor.h" +#include "thread/TaskManager.h" + #include "FontRenderer.h" #include "GammaFontRenderer.h" #include "TextureCache.h" @@ -278,6 +281,8 @@ public: GammaFontRenderer* fontRenderer; + TaskManager tasks; + Dither dither; Stencil stencil; diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index 9e6ec84..afdc2c9 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -31,69 +31,32 @@ namespace uirenderer { // Path precaching /////////////////////////////////////////////////////////////////////////////// -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; +PathCache::PathProcessor::PathProcessor(Caches& caches): + TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) { } -void PathCache::PrecacheThread::addTask(PathTexture* texture, SkPath* path, SkPaint* paint) { - if (!isRunning()) { - run("libhwui:pathPrecache", PRIORITY_DEFAULT); - } - - Task task; - task.texture = texture; - task.path = path; - task.paint = paint; - - Mutex::Autolock l(mLock); - mTasks.add(task); - mSignal.signal(); -} - -void PathCache::PrecacheThread::exit() { - { - Mutex::Autolock l(mLock); - mTasks.clear(); +void PathCache::PathProcessor::onProcess(const sp<Task<SkBitmap*> >& task) { + sp<PathTask> t = static_cast<PathTask* >(task.get()); + ATRACE_NAME("pathPrecache"); + + float left, top, offset; + uint32_t width, height; + PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height); + + PathTexture* texture = t->texture; + texture->left = left; + texture->top = top; + texture->offset = offset; + texture->width = width; + texture->height = height; + + if (width <= mMaxTextureSize && height <= mMaxTextureSize) { + SkBitmap* bitmap = new SkBitmap(); + PathCache::drawPath(t->path, t->paint, *bitmap, left, top, offset, width, height); + t->setResult(bitmap); + } else { + t->setResult(NULL); } - requestExit(); - mSignal.signal(); } /////////////////////////////////////////////////////////////////////////////// @@ -101,11 +64,10 @@ void PathCache::PrecacheThread::exit() { /////////////////////////////////////////////////////////////////////////////// PathCache::PathCache(): ShapeCache<PathCacheEntry>("path", - PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE), mThread(new PrecacheThread()) { + PROPERTY_PATH_CACHE_SIZE, DEFAULT_PATH_CACHE_SIZE) { } PathCache::~PathCache() { - mThread->exit(); } void PathCache::remove(SkPath* path) { @@ -165,17 +127,18 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { } else { // A bitmap is attached to the texture, this means we need to // upload it as a GL texture - if (texture->future() != NULL) { + const sp<Task<SkBitmap*> >& task = texture->task(); + if (task != 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(); + SkBitmap* bitmap = task->getResult(); if (bitmap) { addTexture(entry, bitmap, texture); - texture->clearFuture(); + texture->clearTask(); } else { ALOGW("Path too large to be rendered into a texture (%dx%d)", texture->width, texture->height); - texture->clearFuture(); + texture->clearTask(); texture = NULL; mCache.remove(entry); } @@ -189,6 +152,10 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { } void PathCache::precache(SkPath* path, SkPaint* paint) { + if (!Caches::getInstance().tasks.canRunTasks()) { + return; + } + path = getSourcePath(path); PathCacheEntry entry(path, paint); @@ -205,7 +172,9 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { 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); + texture = createTexture(0.0f, 0.0f, 0.0f, 0, 0, path->getGenerationID()); + sp<PathTask> task = new PathTask(path, paint, texture); + texture->setTask(task); // During the precaching phase we insert path texture objects into // the cache that do not point to any GL texture. They are instead @@ -215,7 +184,11 @@ void PathCache::precache(SkPath* path, SkPaint* paint) { // asks for a path texture. This is also when the cache limit will // be enforced. mCache.put(entry, texture); - mThread->addTask(texture, path, paint); + + if (mProcessor == NULL) { + mProcessor = new PathProcessor(Caches::getInstance()); + } + mProcessor->add(task); } } diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h index 1d28ecb..27031a5 100644 --- a/libs/hwui/PathCache.h +++ b/libs/hwui/PathCache.h @@ -23,6 +23,8 @@ #include "Debug.h" #include "ShapeCache.h" #include "thread/Signal.h" +#include "thread/Task.h" +#include "thread/TaskProcessor.h" class SkPaint; class SkPath; @@ -100,32 +102,33 @@ public: void precache(SkPath* path, SkPaint* paint); private: - class PrecacheThread: public Thread { + class PathTask: public Task<SkBitmap*> { public: - PrecacheThread(): mSignal(Condition::WAKE_UP_ONE) { } + PathTask(SkPath* path, SkPaint* paint, PathTexture* texture): + path(path), paint(paint), texture(texture) { + } - void addTask(PathTexture* texture, SkPath* path, SkPaint* paint); - void exit(); + ~PathTask() { + delete future()->get(); + } - private: - struct Task { - PathTexture* texture; - SkPath* path; - SkPaint* paint; - }; + SkPath* path; + SkPaint* paint; + PathTexture* texture; + }; - virtual bool threadLoop(); + class PathProcessor: public TaskProcessor<SkBitmap*> { + public: + PathProcessor(Caches& caches); + ~PathProcessor() { } - // Lock for the list of tasks - Mutex mLock; - Vector<Task> mTasks; + virtual void onProcess(const sp<Task<SkBitmap*> >& task); - // Signal used to wake up the thread when a new - // task is available in the list - mutable Signal mSignal; + private: + uint32_t mMaxTextureSize; }; - sp<PrecacheThread> mThread; + sp<PathProcessor> mProcessor; Vector<SkPath*> mGarbage; mutable Mutex mLock; }; // class PathCache diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h index 67ae85b..58fea08 100644 --- a/libs/hwui/ShapeCache.h +++ b/libs/hwui/ShapeCache.h @@ -30,12 +30,11 @@ #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" +#include "thread/Task.h" namespace android { namespace uirenderer { @@ -62,14 +61,8 @@ struct PathTexture: public Texture { PathTexture(): Texture() { } - PathTexture(bool hasFuture): Texture() { - if (hasFuture) { - mFuture = new Future<SkBitmap*>(); - } - } - ~PathTexture() { - clearFuture(); + clearTask(); } /** @@ -85,19 +78,22 @@ struct PathTexture: public Texture { */ float offset; - sp<Future<SkBitmap*> > future() const { - return mFuture; + sp<Task<SkBitmap*> > task() const { + return mTask; + } + + void setTask(const sp<Task<SkBitmap*> >& task) { + mTask = task; } - void clearFuture() { - if (mFuture != NULL) { - delete mFuture->get(); - mFuture.clear(); + void clearTask() { + if (mTask != NULL) { + mTask.clear(); } } private: - sp<Future<SkBitmap*> > mFuture; + sp<Task<SkBitmap*> > mTask; }; // struct PathTexture /** @@ -551,8 +547,8 @@ protected: } 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); + uint32_t width, uint32_t height, uint32_t id) { + PathTexture* texture = new PathTexture(); texture->left = left; texture->top = top; texture->offset = offset; diff --git a/libs/hwui/thread/Future.h b/libs/hwui/thread/Future.h index 340fec7..a3ff3bc 100644 --- a/libs/hwui/thread/Future.h +++ b/libs/hwui/thread/Future.h @@ -24,7 +24,7 @@ namespace android { namespace uirenderer { -template<class T> +template<typename T> class Future: public LightRefBase<Future<T> > { public: Future(Condition::WakeUpType type = Condition::WAKE_UP_ONE): mBarrier(type), mResult() { } diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h new file mode 100644 index 0000000..9a211a2 --- /dev/null +++ b/libs/hwui/thread/Task.h @@ -0,0 +1,63 @@ +/* + * 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_TASK_H +#define ANDROID_HWUI_TASK_H + +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <utils/RefBase.h> +#include <utils/Trace.h> + +#include "Future.h" + +namespace android { +namespace uirenderer { + +class TaskBase: public RefBase { +public: + TaskBase() { } + virtual ~TaskBase() { } +}; + +template<typename T> +class Task: public TaskBase { +public: + Task(): mFuture(new Future<T>()) { } + virtual ~Task() { } + + T getResult() const { + ATRACE_NAME("waitForTask"); + return mFuture->get(); + } + + void setResult(T result) { + mFuture->produce(result); + } + +protected: + const sp<Future<T> >& future() const { + return mFuture; + } + +private: + sp<Future<T> > mFuture; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TASK_H diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp new file mode 100644 index 0000000..ce6c8c0 --- /dev/null +++ b/libs/hwui/thread/TaskManager.cpp @@ -0,0 +1,118 @@ +/* + * 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. + */ + +#include <sys/sysinfo.h> + +#include "Task.h" +#include "TaskProcessor.h" +#include "TaskManager.h" + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Manager +/////////////////////////////////////////////////////////////////////////////// + +TaskManager::TaskManager() { + // Get the number of available CPUs. This value does not change over time. + int cpuCount = sysconf(_SC_NPROCESSORS_ONLN); + + for (int i = 0; i < cpuCount / 2; i++) { + String8 name; + name.appendFormat("hwuiTask%d", i + 1); + mThreads.add(new WorkerThread(name)); + } +} + +TaskManager::~TaskManager() { + for (size_t i = 0; i < mThreads.size(); i++) { + mThreads[i]->exit(); + } +} + +bool TaskManager::canRunTasks() const { + return mThreads.size() > 0; +} + +bool TaskManager::addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor) { + if (mThreads.size() > 0) { + TaskWrapper wrapper(task, processor); + + size_t minQueueSize = INT_MAX; + sp<WorkerThread> thread; + + for (size_t i = 0; i < mThreads.size(); i++) { + if (mThreads[i]->getTaskCount() < minQueueSize) { + thread = mThreads[i]; + minQueueSize = mThreads[i]->getTaskCount(); + } + } + + return thread->addTask(wrapper); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Thread +/////////////////////////////////////////////////////////////////////////////// + +bool TaskManager::WorkerThread::threadLoop() { + mSignal.wait(); + Vector<TaskWrapper> tasks; + { + Mutex::Autolock l(mLock); + tasks = mTasks; + mTasks.clear(); + } + + for (size_t i = 0; i < tasks.size(); i++) { + const TaskWrapper& task = tasks.itemAt(i); + task.mProcessor->process(task.mTask); + } + + return true; +} + +bool TaskManager::WorkerThread::addTask(TaskWrapper task) { + if (!isRunning()) { + run(mName.string(), PRIORITY_DEFAULT); + } + + Mutex::Autolock l(mLock); + ssize_t index = mTasks.add(task); + mSignal.signal(); + + return index >= 0; +} + +size_t TaskManager::WorkerThread::getTaskCount() const { + Mutex::Autolock l(mLock); + return mTasks.size(); +} + +void TaskManager::WorkerThread::exit() { + { + Mutex::Autolock l(mLock); + mTasks.clear(); + } + requestExit(); + mSignal.signal(); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/thread/TaskManager.h b/libs/hwui/thread/TaskManager.h new file mode 100644 index 0000000..bc86062 --- /dev/null +++ b/libs/hwui/thread/TaskManager.h @@ -0,0 +1,100 @@ +/* + * 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_TASK_MANAGER_H +#define ANDROID_HWUI_TASK_MANAGER_H + +#include <utils/Mutex.h> +#include <utils/String8.h> +#include <utils/Thread.h> +#include <utils/Vector.h> + +#include "Signal.h" + +namespace android { +namespace uirenderer { + +template <typename T> +class Task; +class TaskBase; + +template <typename T> +class TaskProcessor; +class TaskProcessorBase; + +class TaskManager { +public: + TaskManager(); + ~TaskManager(); + + /** + * Returns true if this task manager can run tasks, + * false otherwise. This method will typically return + * true on a single CPU core device. + */ + bool canRunTasks() const; + +private: + template <typename T> + friend class TaskProcessor; + + template<typename T> + bool addTask(const sp<Task<T> >& task, const sp<TaskProcessor<T> >& processor) { + return addTaskBase(sp<TaskBase>(task), sp<TaskProcessorBase>(processor)); + } + + bool addTaskBase(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor); + + struct TaskWrapper { + TaskWrapper(): mTask(), mProcessor() { } + + TaskWrapper(const sp<TaskBase>& task, const sp<TaskProcessorBase>& processor): + mTask(task), mProcessor(processor) { + } + + sp<TaskBase> mTask; + sp<TaskProcessorBase> mProcessor; + }; + + class WorkerThread: public Thread { + public: + WorkerThread(const String8 name): mSignal(Condition::WAKE_UP_ONE), mName(name) { } + + bool addTask(TaskWrapper task); + size_t getTaskCount() const; + void exit(); + + private: + virtual bool threadLoop(); + + // Lock for the list of tasks + mutable Mutex mLock; + Vector<TaskWrapper> mTasks; + + // Signal used to wake up the thread when a new + // task is available in the list + mutable Signal mSignal; + + const String8 mName; + }; + + Vector<sp<WorkerThread> > mThreads; +}; + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TASK_MANAGER_H diff --git a/libs/hwui/thread/TaskProcessor.h b/libs/hwui/thread/TaskProcessor.h new file mode 100644 index 0000000..d1269f0 --- /dev/null +++ b/libs/hwui/thread/TaskProcessor.h @@ -0,0 +1,72 @@ +/* + * 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_TASK_PROCESSOR_H +#define ANDROID_HWUI_TASK_PROCESSOR_H + +#include <utils/RefBase.h> + +#include "Task.h" +#include "TaskManager.h" + +namespace android { +namespace uirenderer { + +class TaskProcessorBase: public RefBase { +public: + TaskProcessorBase() { } + virtual ~TaskProcessorBase() { }; + +private: + friend class TaskManager; + + virtual void process(const sp<TaskBase>& task) = 0; +}; + +template<typename T> +class TaskProcessor: public TaskProcessorBase { +public: + TaskProcessor(TaskManager* manager): mManager(manager) { } + virtual ~TaskProcessor() { } + + bool add(const sp<Task<T> >& task); + + virtual void onProcess(const sp<Task<T> >& task) = 0; + +private: + virtual void process(const sp<TaskBase>& task) { + sp<Task<T> > realTask = static_cast<Task<T>* >(task.get()); + // This is the right way to do it but sp<> doesn't play nice + // sp<Task<T> > realTask = static_cast<sp<Task<T> > >(task); + onProcess(realTask); + } + + TaskManager* mManager; +}; + +template<typename T> +bool TaskProcessor<T>::add(const sp<Task<T> >& task) { + if (mManager) { + sp<TaskProcessor<T> > self(this); + return mManager->addTask(task, self); + } + return false; +} + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_TASK_PROCESSOR_H |