From d88d618cc9e5f7627c7adde0f46c154ba31650eb Mon Sep 17 00:00:00 2001 From: subhransu mohanty Date: Fri, 14 Jun 2019 17:20:06 +0900 Subject: [PATCH] rlottie: Refactor Rle Generation to optimize memory allocation. 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 | 37 ++---- src/lottie/lottieitem.h | 7 +- src/vector/vdrawable.cpp | 20 +-- src/vector/vdrawable.h | 3 +- src/vector/vraster.cpp | 254 +++++++++++++++++++++++--------------- src/vector/vraster.h | 52 ++------ 6 files changed, 182 insertions(+), 191 deletions(-) diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp index 601dbe3..99b5648 100644 --- a/src/lottie/lottieitem.cpp +++ b/src/lottie/lottieitem.cpp @@ -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>(); - - 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>(); - - 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() diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h index d75cfd3..73eeb7c 100644 --- a/src/lottie/lottieitem.h +++ b/src/lottie/lottieitem.h @@ -81,8 +81,7 @@ public: public: VSize mSize; VPath mPath; - RleShare mRleFuture; - VRle mRle; + VRasterizer mRasterizer; }; typedef vFlag DirtyFlag; @@ -215,8 +214,8 @@ public: VMatrix mCombinedMatrix; VPath mLocalPath; VPath mFinalPath; - RleShare mRleFuture; - VRle mRle; + VRasterizer mRasterizer; + bool mRasterRequest{false}; }; /* diff --git a/src/vector/vdrawable.cpp b/src/vector/vdrawable.cpp index dc933f5..775564c 100644 --- a/src/vector/vdrawable.cpp +++ b/src/vector/vdrawable.cpp @@ -23,25 +23,16 @@ void VDrawable::preprocess(const VRect &clip) { if (mFlag & (DirtyState::Path)) { - - if (!mRleFuture) mRleFuture = std::make_shared>(); - - 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, diff --git a/src/vector/vdrawable.h b/src/vector/vdrawable.h index 5b0eac1..4e9bf03 100644 --- a/src/vector/vdrawable.h +++ b/src/vector/vdrawable.h @@ -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}; diff --git a/src/vector/vraster.cpp b/src/vector/vraster.cpp index 8e6c2c7..63ea988 100644 --- a/src/vector/vraster.cpp +++ b/src/vector/vraster.cpp @@ -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 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 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, ¶ms); -} - -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, ¶ms); } - 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; #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 _threads; - std::vector> _q{_count}; + std::vector> _q{_count}; std::atomic _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(); +} + +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 diff --git a/src/vector/vraster.h b/src/vector/vraster.h index a858b63..987f9ad 100644 --- a/src/vector/vraster.h +++ b/src/vector/vraster.h @@ -27,50 +27,18 @@ V_BEGIN_NAMESPACE class VPath; class VRle; -template -class VSharedState { +class VRasterizer +{ public: - void set_value(R value) { - if (_ready) return; - - { - std::lock_guard lock(_mutex); - _value = std::move(value); - _ready = true; - } - _cv.notify_one(); - } - R get(){ - std::unique_lock 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>; - -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 d{nullptr}; }; V_END_NAMESPACE -- 2.34.1