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

Commit c717bbe7 authored by Romain Guy's avatar Romain Guy Committed by Android Git Automerger
Browse files

am 342afc2a: am 2a2ead93: Merge "Precache paths from a worker thread" into jb-mr2-dev

* commit '342afc2a':
  Precache paths from a worker thread
parents 607dceda 342afc2a
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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));
    }
+0 −1
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
#define LOG_TAG "OpenGLRenderer"

#include <utils/JenkinsHash.h>
#include <utils/threads.h>

#include "Caches.h"
#include "Debug.h"
+6 −0
Original line number Diff line number Diff line
@@ -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;
+139 −23
Original line number Diff line number Diff line
@@ -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();
    }

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

    left = bounds.fLeft;
    top = bounds.fTop;
        if (width <= maxSize && height <= maxSize) {
            SkBitmap* bitmap = new SkBitmap();

    offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f);
            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;
}

    width = uint32_t(pathWidth + offset * 2.0 + 0.5);
    height = uint32_t(pathHeight + offset * 2.0 + 0.5);
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();
}

///////////////////////////////////////////////////////////////////////////////
@@ -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 {
        // 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
+36 −0
Original line number Diff line number Diff line
@@ -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
Loading