rlottie: Refactor Rle Generation to optimize memory allocation.
authorsubhransu mohanty <sub.mohanty@samsung.com>
Fri, 14 Jun 2019 08:20:06 +0000 (17:20 +0900)
committerHermet Park <hermetpark@gmail.com>
Wed, 19 Jun 2019 04:36:02 +0000 (13:36 +0900)
Moved the Rle Generation to Rasterizer class which will take care of
handling async rle generation efficiently by reusing Task.
Now Rle Task scheduler will take a shared_ptr to the Task data which will
be created once in Rasterizer.

src/lottie/lottieitem.cpp
src/lottie/lottieitem.h
src/vector/vdrawable.cpp
src/vector/vdrawable.h
src/vector/vraster.cpp
src/vector/vraster.h

index 601dbe3228b7954a6cffa14bc13458126b40e735..99b56486ff7d8592e447e8e03d362ee6b4bd8ac4 100644 (file)
@@ -210,26 +210,19 @@ void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix,
     mFinalPath.clone(mLocalPath);
     mFinalPath.transform(parentMatrix);
 
-    VPath tmp = mFinalPath;
-
-    if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
-
-    if (mRleFuture->valid()) mRle = mRleFuture->get();
-    mRleFuture->reuse();
-
-    VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
-    mRle = VRle();
+    mRasterizer.rasterize(mFinalPath);
+    mRasterRequest = true;
 }
 
 VRle LOTMaskItem::rle()
 {
-    if (mRleFuture && mRleFuture->valid()) {
-        mRle = mRleFuture->get();
+    if (mRasterRequest) {
+        mRasterRequest = false;
         if (!vCompare(mCombinedAlpha, 1.0f))
-            mRle *= (mCombinedAlpha * 255);
-        if (mData->mInv) mRle.invert();
+            mRasterizer.rle() *= (mCombinedAlpha * 255);
+        if (mData->mInv) mRasterizer.rle().invert();
     }
-    return mRle;
+    return mRasterizer.rle();
 }
 
 void LOTLayerItem::buildLayerNode()
@@ -703,24 +696,12 @@ void LOTClipperItem::update(const VMatrix &matrix)
     mPath.reset();
     mPath.addRect(VRectF(0,0, mSize.width(), mSize.height()));
     mPath.transform(matrix);
-
-    VPath tmp = mPath;
-
-    if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
-
-    if (mRleFuture->valid()) mRle = mRleFuture->get();
-    mRleFuture->reuse();
-
-    VRaster::generateFillInfo(mRleFuture, std::move(tmp), std::move(mRle));
-    mRle = VRle();
+    mRasterizer.rasterize(mPath);
 }
 
 VRle LOTClipperItem::rle()
 {
-    if (mRleFuture && mRleFuture->valid()) {
-        mRle = mRleFuture->get();
-    }
-    return mRle;
+    return mRasterizer.rle();
 }
 
 void LOTCompLayerItem::updateContent()
index d75cfd3fb4746c4c065ad2d3c9c05421b3e06531..73eeb7c7f20ca353988a10fda5899898af74c438 100644 (file)
@@ -81,8 +81,7 @@ public:
 public:
     VSize                    mSize;
     VPath                    mPath;
-    RleShare                 mRleFuture;
-    VRle                     mRle;
+    VRasterizer              mRasterizer;
 };
 
 typedef vFlag<DirtyFlagBit> DirtyFlag;
@@ -215,8 +214,8 @@ public:
     VMatrix                  mCombinedMatrix;
     VPath                    mLocalPath;
     VPath                    mFinalPath;
-    RleShare                 mRleFuture;
-    VRle                     mRle;
+    VRasterizer              mRasterizer;
+    bool                     mRasterRequest{false};
 };
 
 /*
index dc933f5c906e9c2b16983d5cfc308ea73cdbe88b..775564c251e3cf446f59e71af00c8b5812299158 100644 (file)
 void VDrawable::preprocess(const VRect &clip)
 {
     if (mFlag & (DirtyState::Path)) {
-
-        if (!mRleFuture) mRleFuture = std::make_shared<VSharedState<VRle>>();
-
-        if (mRleFuture->valid()) mRle = mRleFuture->get();
-        mRleFuture->reuse();
-
         if (mStroke.enable) {
             if (mStroke.mDash.size()) {
                 VDasher dasher(mStroke.mDash.data(), mStroke.mDash.size());
                 mPath = dasher.dashed(mPath);
             }
-            VRaster::generateStrokeInfo(mRleFuture,
-                std::move(mPath), std::move(mRle), mStroke.cap, mStroke.join,
-                mStroke.width, mStroke.meterLimit, clip);
+            mRasterizer.rasterize(std::move(mPath), mStroke.cap, mStroke.join,
+                                  mStroke.width, mStroke.meterLimit, clip);
         } else {
-            VRaster::generateFillInfo(mRleFuture,
-                std::move(mPath), std::move(mRle), mFillRule, clip);
+            mRasterizer.rasterize(std::move(mPath), mFillRule, clip);
         }
-        mRle = {};
         mPath = {};
         mFlag &= ~DirtyFlag(DirtyState::Path);
     }
@@ -49,10 +40,7 @@ void VDrawable::preprocess(const VRect &clip)
 
 VRle VDrawable::rle()
 {
-    if (mRleFuture && mRleFuture->valid()) {
-        mRle = mRleFuture->get();
-    }
-    return mRle;
+    return mRasterizer.rle();
 }
 
 void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit,
index 5b0eac1f3b96619aba395b67296b03fbadbee724..4e9bf03cd502b05cc29c769aadfae20e40dfb095 100644 (file)
@@ -57,10 +57,9 @@ public:
         CapStyle           cap{CapStyle::Flat};
         JoinStyle          join{JoinStyle::Bevel};
     };
+    VRasterizer       mRasterizer;
     VBrush            mBrush;
     VPath             mPath;
-    RleShare          mRleFuture;
-    VRle              mRle;
     StrokeInfo        mStroke;
     DirtyFlag         mFlag{DirtyState::All};
     FillRule          mFillRule{FillRule::Winding};
index 8e6c2c78366c3c87786dc296100b39b7a16d8307..63ea988d726e34da9ac534ff7e8fce4c5f7648cf 100644 (file)
@@ -254,105 +254,136 @@ static void bboxCb(int x, int y, int w, int h, void *user)
     rle->setBoundingRect({x, y, w, h});
 }
 
-struct RleTask {
-    RleShare           mRlePromise;
-    VPath              path;
-    VRle               rle;
-    float              width;
-    float              meterLimit;
-    VRect              clip;
-    FillRule           fillRule;
-    CapStyle           cap;
-    JoinStyle          join;
-    bool               stroke;
-    VRle               operator()(FTOutline &outRef, SW_FT_Stroker &stroker);
-    void               render(FTOutline &outRef);
-    RleTask() {}
-    RleTask(RleShare &apromise, VPath &&apath, VRle &&arle, FillRule afillRule, const VRect &aclip)
-    {
-        path = std::move(apath);
-        rle = std::move(arle);
-        fillRule = afillRule;
-        clip = aclip;
-        stroke = false;
-        mRlePromise = apromise;
+
+class SharedRle {
+public:
+    VRle& unsafe(){ return _rle;}
+    void notify() {
+        {
+            std::lock_guard<std::mutex> lock(_mutex);
+            _ready = true;
+        }
+        _cv.notify_one();
     }
-    RleTask(RleShare &apromise, VPath &&apath, VRle &&arle, CapStyle acap, JoinStyle ajoin,
-            float awidth, float ameterLimit, const VRect &aclip)
-    {
-        stroke = true;
-        path = std::move(apath);
-        rle = std::move(arle);
-        cap = acap;
-        join = ajoin;
-        width = awidth;
-        meterLimit = ameterLimit;
-        clip = aclip;
-        mRlePromise = apromise;
+    VRle& get(){
+
+        if (!_pending) return _rle;
+
+        std::unique_lock<std::mutex> lock(_mutex);
+        while(!_ready) _cv.wait(lock);
+        _pending = false;
+        return _rle;
     }
 
+    void reset() {
+        _ready = false;
+        _pending = true;
+    }
+private:
+    VRle                     _rle;
+    std::mutex               _mutex;
+    std::condition_variable  _cv;
+    bool                     _ready{true};
+    bool                     _pending{false};
 };
 
-void RleTask::render(FTOutline &outRef)
-{
-    SW_FT_Raster_Params params;
-
-    params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
-    params.gray_spans = &rleGenerationCb;
-    params.bbox_cb = &bboxCb;
-    params.user = &rle;
-    params.source = &outRef.ft;
+struct VRleTask {
+    SharedRle     mRle;
+    VPath         mPath;
+    float         mStrokeWidth;
+    float         mMeterLimit;
+    VRect         mClip;
+    FillRule      mFillRule;
+    CapStyle      mCap;
+    JoinStyle     mJoin;
+    bool          mGenerateStroke;
 
-    if (!clip.empty()) {
-        params.flags |= SW_FT_RASTER_FLAG_CLIP;
+    VRle& rle() { return mRle.get();}
 
-        params.clip_box.xMin =  clip.left();
-        params.clip_box.yMin =  clip.top();
-        params.clip_box.xMax =  clip.right();
-        params.clip_box.yMax =  clip.bottom();
+    void update(VPath path, FillRule fillRule, const VRect &clip)
+    {
+        mRle.reset();
+        mPath = std::move(path);
+        mFillRule = fillRule;
+        mClip = clip;
+        mGenerateStroke = false;
     }
-    // compute rle
-    sw_ft_grays_raster.raster_render(nullptr, &params);
-}
-
-VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
-{
-    rle.reset();
-    if (stroke) {  // Stroke Task
-        outRef.convert(path);
-        outRef.convert(cap, join, width, meterLimit);
 
-        uint points, contors;
+    void update(VPath path, CapStyle cap, JoinStyle join, float width, float meterLimit, const VRect &clip)
+    {
+        mRle.reset();
+        mPath = std::move(path);
+        mCap = cap;
+        mJoin = join;
+        mStrokeWidth = width;
+        mMeterLimit = meterLimit;
+        mClip = clip;
+        mGenerateStroke = true;
+    }
+    void render(FTOutline &outRef)
+    {
+        SW_FT_Raster_Params params;
 
-        SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
-                          outRef.ftMeterLimit);
-        SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
-        SW_FT_Stroker_GetCounts(stroker, &points, &contors);
+        mRle.unsafe().reset();
 
-        outRef.grow(points, contors);
+        params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA;
+        params.gray_spans = &rleGenerationCb;
+        params.bbox_cb = &bboxCb;
+        params.user = &mRle.unsafe();
+        params.source = &outRef.ft;
 
-        SW_FT_Stroker_Export(stroker, &outRef.ft);
+        if (!mClip.empty()) {
+            params.flags |= SW_FT_RASTER_FLAG_CLIP;
 
-    } else {  // Fill Task
-        outRef.convert(path);
-        int fillRuleFlag = SW_FT_OUTLINE_NONE;
-        switch (fillRule) {
-        case FillRule::EvenOdd:
-            fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
-            break;
-        default:
-            fillRuleFlag = SW_FT_OUTLINE_NONE;
-            break;
+            params.clip_box.xMin =  mClip.left();
+            params.clip_box.yMin =  mClip.top();
+            params.clip_box.xMax =  mClip.right();
+            params.clip_box.yMax =  mClip.bottom();
         }
-        outRef.ft.flags = fillRuleFlag;
+        // compute rle
+        sw_ft_grays_raster.raster_render(nullptr, &params);
     }
 
-    render(outRef);
+    void operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
+    {
+        if (mGenerateStroke) {  // Stroke Task
+            outRef.convert(mPath);
+            outRef.convert(mCap, mJoin, mStrokeWidth, mMeterLimit);
+
+            uint points, contors;
+
+            SW_FT_Stroker_Set(stroker, outRef.ftWidth, outRef.ftCap, outRef.ftJoin,
+                              outRef.ftMeterLimit);
+            SW_FT_Stroker_ParseOutline(stroker, &outRef.ft);
+            SW_FT_Stroker_GetCounts(stroker, &points, &contors);
+
+            outRef.grow(points, contors);
+
+            SW_FT_Stroker_Export(stroker, &outRef.ft);
+
+        } else {  // Fill Task
+            outRef.convert(mPath);
+            int fillRuleFlag = SW_FT_OUTLINE_NONE;
+            switch (mFillRule) {
+            case FillRule::EvenOdd:
+                fillRuleFlag = SW_FT_OUTLINE_EVEN_ODD_FILL;
+                break;
+            default:
+                fillRuleFlag = SW_FT_OUTLINE_NONE;
+                break;
+            }
+            outRef.ft.flags = fillRuleFlag;
+        }
 
-    path = VPath();
+        render(outRef);
 
-    return std::move(rle);
-}
+        mPath = VPath();
+
+        mRle.notify();
+    }
+};
+
+using VTask = std::shared_ptr<VRleTask>;
 
 #ifdef LOTTIE_THREAD_SUPPORT
 
@@ -362,7 +393,7 @@ VRle RleTask::operator()(FTOutline &outRef, SW_FT_Stroker &stroker)
 class RleTaskScheduler {
     const unsigned                  _count{std::thread::hardware_concurrency()};
     std::vector<std::thread>        _threads;
-    std::vector<TaskQueue<RleTask>> _q{_count};
+    std::vector<TaskQueue<VTask>>   _q{_count};
     std::atomic<unsigned>           _index{0};
 
     void run(unsigned i)
@@ -375,7 +406,7 @@ class RleTaskScheduler {
         SW_FT_Stroker_New(&stroker);
 
         // Task Loop
-        RleTask task;
+        VTask task;
         while (true) {
             bool success = false;
 
@@ -388,7 +419,7 @@ class RleTaskScheduler {
 
             if (!success && !_q[i].pop(task)) break;
 
-            task.mRlePromise->set_value((task)(outlineRef, stroker));
+            (*task)(outlineRef, stroker);
         }
 
         // cleanup
@@ -415,7 +446,7 @@ public:
         for (auto &e : _threads) e.join();
     }
 
-    void process(RleTask &&task)
+    void process(VTask task)
     {
         auto i = _index++;
 
@@ -452,34 +483,59 @@ public:
         SW_FT_Stroker_Done(stroker);
     }
 
-    void process(RleTask &&task)
+    void process(VTask task)
     {
-        task.mRlePromise->set_value((task)(outlineRef, stroker));
+        (*task)(outlineRef, stroker);
     }
 };
 #endif
 
-void VRaster::generateFillInfo(RleShare &promise, VPath &&path, VRle &&rle,
-                                            FillRule fillRule, const VRect &clip)
+
+struct VRasterizer::VRasterizerImpl
+{
+    VRleTask mTask;
+
+    VRle& rle(){ return mTask.rle(); }
+    VRleTask& task(){ return mTask; }
+};
+
+VRle VRasterizer::rle()
+{
+    if(!d) return VRle();
+    return d->rle();
+}
+
+void VRasterizer::init()
+{
+    if(!d) d = std::make_shared<VRasterizerImpl>();
+}
+
+void VRasterizer::updateRequest()
+{
+    VTask taskObj = VTask(d, &d->task());
+    RleTaskScheduler::instance().process(std::move(taskObj));
+}
+
+void VRasterizer::rasterize(VPath path, FillRule fillRule, const VRect &clip)
 {
+    init();
     if (path.empty()) {
-        rle.reset();
-        promise->set_value(rle);
+        d->rle().reset();
         return;
     }
-    return RleTaskScheduler::instance().process(RleTask(promise, std::move(path), std::move(rle), fillRule, clip));
+    d->task().update(std::move(path), fillRule, clip);
+    updateRequest();
 }
 
-void VRaster::generateStrokeInfo(RleShare &promise, VPath &&path, VRle &&rle, CapStyle cap,
-                                 JoinStyle join, float width,
-                                 float meterLimit, const VRect &clip)
+void VRasterizer::rasterize(VPath path, CapStyle cap, JoinStyle join, float width, float meterLimit, const VRect &clip)
 {
-    if (path.empty() || vCompare(width, 0.0f)) {
-        rle.reset();
-        promise->set_value(rle);
+    init();
+    if (path.empty() || vIsZero(width)) {
+        d->rle().reset();
         return;
     }
-    return RleTaskScheduler::instance().process(RleTask(promise, std::move(path), std::move(rle), cap, join, width, meterLimit, clip));
+    d->task().update(std::move(path), cap, join, width, meterLimit, clip);
+    updateRequest();
 }
 
 V_END_NAMESPACE
index a858b637008103dd564f357c1277e58a654e21ff..987f9ad2bf2bee71ebb24438f06048e7c29d110a 100644 (file)
@@ -27,50 +27,18 @@ V_BEGIN_NAMESPACE
 class VPath;
 class VRle;
 
-template <class R>
-class VSharedState {
+class VRasterizer
+{
 public:
-    void set_value(R value) {
-        if (_ready) return;
-
-        {
-            std::lock_guard<std::mutex> lock(_mutex);
-            _value = std::move(value);
-            _ready = true;
-        }
-        _cv.notify_one();
-    }
-    R get(){
-        std::unique_lock<std::mutex> lock(_mutex);
-        while(!_ready) _cv.wait(lock);
-        _valid = false;
-        return std::move(_value);
-    }
-    bool valid() const {return _valid;}
-    void reuse() {
-        _ready = false;
-        _valid = true;
-    }
+    void rasterize(VPath path, FillRule fillRule = FillRule::Winding, const VRect &clip = VRect());
+    void rasterize(VPath path, CapStyle cap, JoinStyle join, float width,
+                   float meterLimit, const VRect &clip = VRect());
+    VRle rle();
 private:
-    R                        _value;
-    std::mutex               _mutex;
-    std::condition_variable  _cv;
-    bool                     _ready{false};
-    bool                     _valid{false};
-};
-
-using RleShare = std::shared_ptr<VSharedState<VRle>>;
-
-struct VRaster {
-
-    static void
-    generateFillInfo(RleShare &promise, VPath &&path, VRle &&rle,
-                     FillRule fillRule = FillRule::Winding, const VRect &clip = VRect());
-
-    static void
-    generateStrokeInfo(RleShare &promise, VPath &&path, VRle &&rle,
-                       CapStyle cap, JoinStyle join,
-                       float width, float meterLimit, const VRect &clip = VRect());
+    struct VRasterizerImpl;
+    void init();
+    void updateRequest();
+    std::shared_ptr<VRasterizerImpl> d{nullptr};
 };
 
 V_END_NAMESPACE