Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5dc7fa70 authored by Romain Guy's avatar Romain Guy
Browse files

Add TaskManager API

This API can be used to run arbitrary tasks on a pool of worker
threads. The number of threads is calculated based on the number
of CPU cores available.

The API is made of 3 classes:

TaskManager
      Creates and manages the worker threads.

Task
      Describes the work to be done and the type of the output.
      A task contains a future used to wait for the worker thread
      to be done computing the result of the task.

TaskProcessor
      The processor dispatches tasks to the TaskManager and is
      responsible for performing the computation required by
      each task. A processor will only be asked to process tasks
      sent to the manager through the processor.

A typical use case:

class MyTask: Task<MyType>

class MyProcessor: TaskProcessor<MyType>

TaskManager m = new TaskManager();
MyProcessor p = new MyProcessor(m);
MyTask t = new MyTask();
p.add(t);

// Waits until the result is available
MyType result = t->getResult();

Change-Id: I1fe845ba4c49bb0e1b0627ab147f9a861c8e0749
parent 8818d84a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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 \
+5 −0
Original line number Diff line number Diff line
@@ -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;

+41 −68
Original line number Diff line number Diff line
@@ -31,81 +31,43 @@ namespace uirenderer {
// Path precaching
///////////////////////////////////////////////////////////////////////////////

bool PathCache::PrecacheThread::threadLoop() {
    mSignal.wait();
    Vector<Task> tasks;
    {
        Mutex::Autolock l(mLock);
        tasks = mTasks;
        mTasks.clear();
PathCache::PathProcessor::PathProcessor(Caches& caches):
        TaskProcessor<SkBitmap*>(&caches.tasks), mMaxTextureSize(caches.maxTextureSize) {
}

    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);
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(task.path, task.paint, left, top, offset, width, height);

        if (width <= maxSize && height <= maxSize) {
            SkBitmap* bitmap = new SkBitmap();
    PathCache::computePathBounds(t->path, t->paint, left, top, offset, width, height);

            PathTexture* texture = task.texture;
    PathTexture* texture = t->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);
    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 {
            task.texture->future()->produce(NULL);
        t->setResult(NULL);
    }
}
    ATRACE_END();
    return true;
}

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();
    }
    requestExit();
    mSignal.signal();
}

///////////////////////////////////////////////////////////////////////////////
// Path cache
///////////////////////////////////////////////////////////////////////////////

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);
    }
}

+21 −18
Original line number Diff line number Diff line
@@ -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;
        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
+14 −18
Original line number Diff line number Diff line
@@ -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;
Loading