From 1728e6955f97246b7af734da66677dad2294d2f1 Mon Sep 17 00:00:00 2001 From: subhransu mohanty Date: Wed, 18 Jul 2018 14:03:23 +0900 Subject: [PATCH] lottie:render: added task stealing scheduler for async rle generation. Change-Id: Ie5a48bb018db8bccef499aac5cb7788de5df5b61 --- src/lottie/lottieitem.cpp | 50 ++++++++---- src/lottie/lottieitem.h | 18 +++-- src/vector/vraster.cpp | 190 ++++++++++++++++++++++++++++++++++++---------- src/vector/vraster.h | 6 +- 4 files changed, 196 insertions(+), 68 deletions(-) diff --git a/src/lottie/lottieitem.cpp b/src/lottie/lottieitem.cpp index 0d62d4d..86bda24 100644 --- a/src/lottie/lottieitem.cpp +++ b/src/lottie/lottieitem.cpp @@ -27,18 +27,24 @@ void VDrawable::preprocess() newPath = dasher.dashed(mPath); } FTOutline *outline = VRaster::toFTOutline(newPath); - mRle = VRaster::instance().generateStrokeInfo(outline, mStroke.cap, mStroke.join, - mStroke.width, mStroke.meterLimit); - VRaster::deleteFTOutline(outline); + mRleTask = VRaster::instance().generateStrokeInfo(outline, mStroke.cap, mStroke.join, + mStroke.width, mStroke.meterLimit); } else { FTOutline *outline = VRaster::toFTOutline(mPath); - mRle = VRaster::instance().generateFillInfo(outline, mFillRule); - VRaster::deleteFTOutline(outline); + mRleTask = VRaster::instance().generateFillInfo(outline, mFillRule); } mFlag &= ~DirtyFlag(DirtyState::Path); } } +VRle VDrawable::rle() +{ + if (mRleTask.valid()) { + mRle = std::move(mRleTask.get()); + } + return mRle; +} + void VDrawable::setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth) { mStroke.enable = true; @@ -310,19 +316,25 @@ void LOTMaskItem::update(int frameNo, const VMatrix &parentMatrix, } float opacity = mData->opacity(frameNo); opacity = opacity * parentAlpha; + mCombinedAlpha = opacity; VPath path = mLocalPath; path.transform(parentMatrix); FTOutline *outline = VRaster::toFTOutline(path); - mRle = VRaster::instance().generateFillInfo(outline); - VRaster::deleteFTOutline(outline); - - mRle = mRle * (opacity * 255); + mRleTask = VRaster::instance().generateFillInfo(outline); +} - if (mData->mInv) { - mRle = ~mRle; +VRle LOTMaskItem::rle() +{ + if (mRleTask.valid()) { + mRle = std::move(mRleTask.get()); + if (!vCompare(mCombinedAlpha, 1.0f)) + mRle = mRle * (mCombinedAlpha * 255); + if (mData->mInv) + mRle = ~mRle; } + return mRle; } void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask) @@ -336,35 +348,41 @@ void LOTLayerItem::render(VPainter *painter, const VRle &inheritMask) else mask = mask & inheritMask; } + + // do preprocessing first to take advantage of thread pool. for(auto i : list) { i->preprocess(); + } + + for(auto i : list) { painter->setBrush(i->mBrush); if (!mask.isEmpty()) { - VRle rle = i->mRle & mask; + VRle rle = i->rle() & mask; painter->drawRle(VPoint(), rle); } else { - painter->drawRle(VPoint(), i->mRle); + painter->drawRle(VPoint(), i->rle()); } } } VRle LOTLayerItem::maskRle(const VRect &clipRect) { + VRle rle; for (auto &i : mMasks) { switch (i->maskMode()) { case LOTMaskData::Mode::Add: { - rle = rle + i->mRle; + rle = rle + i->rle(); break; } case LOTMaskData::Mode::Substarct: { if (rle.isEmpty() && !clipRect.isEmpty()) rle = VRle::toRle(clipRect); - rle = rle - i->mRle; + rle = rle - i->rle(); break; } case LOTMaskData::Mode::Intersect: { - rle = rle & i->mRle; + rle = rle & i->rle(); break; } default: diff --git a/src/lottie/lottieitem.h b/src/lottie/lottieitem.h index 72b7b23..5054b6a 100644 --- a/src/lottie/lottieitem.h +++ b/src/lottie/lottieitem.h @@ -144,12 +144,14 @@ public: LOTMaskItem(LOTMaskData *data): mData(data), mCombinedAlpha(0){} void update(int frameNo, const VMatrix &parentMatrix, float parentAlpha, const DirtyFlag &flag); LOTMaskData::Mode maskMode() const { return mData->mMode;} + VRle rle(); public: - LOTMaskData *mData; - float mCombinedAlpha; - VMatrix mCombinedMatrix; - VPath mLocalPath; - VRle mRle; + LOTMaskData *mData; + float mCombinedAlpha; + VMatrix mCombinedMatrix; + VPath mLocalPath; + std::future mRleTask; + VRle mRle; }; class VDrawable @@ -175,13 +177,15 @@ public: void setStrokeInfo(CapStyle cap, JoinStyle join, float meterLimit, float strokeWidth); void setDashInfo(float *array, int size); void preprocess(); + VRle rle(); public: DirtyFlag mFlag; VDrawable::Type mType; VBrush mBrush; - VPath mPath; + VPath mPath; FillRule mFillRule; - VRle mRle; + std::future mRleTask; + VRle mRle; struct { bool enable; float width; diff --git a/src/vector/vraster.cpp b/src/vector/vraster.cpp index 7a5a892..1d67f2a 100644 --- a/src/vector/vraster.cpp +++ b/src/vector/vraster.cpp @@ -5,6 +5,8 @@ #include"vmatrix.h" #include #include"vdebug.h" +#include"vtaskqueue.h" +#include V_BEGIN_NAMESPACE @@ -36,6 +38,8 @@ public: bool closed; }; + + #define TO_FT_COORD(x) ((x) * 64) // to freetype 26.6 coordinate. void FTOutline::transform(const VMatrix &m) @@ -133,54 +137,153 @@ rleGenerationCb( int count, const SW_FT_Span* spans,void *user) rle->addSpan(rleSpan, count); } -VRle generateFillInfoAsync(const SW_FT_Outline *outline) +struct RleTask { - VRle rle; - SW_FT_Raster_Params params; + RleTask() { + receiver = sender.get_future(); + } + std::promise sender; + std::future receiver; + bool stroke; + FTOutline *outline; + SW_FT_Stroker_LineCap cap; + SW_FT_Stroker_LineJoin join; + int width; + int meterLimit; + SW_FT_Bool closed; +}; - params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ; - params.gray_spans = &rleGenerationCb; - params.user = &rle; - params.source = outline; +static VRle generateRleAsync(RleTask *task); - sw_ft_grays_raster.raster_render(nullptr, ¶ms); +class RleTaskScheduler { + const unsigned _count{std::thread::hardware_concurrency()}; + std::vector _threads; + std::vector> _q{_count}; + std::atomic _index{0}; - return rle; -} + void run(unsigned i) { + while (true) { + RleTask *task = nullptr; + + for (unsigned n = 0; n != _count * 32; ++n) { + if (_q[(i + n) % _count].try_pop(task)) break; + } + if (!task && !_q[i].pop(task)) break; + + VRle rle = generateRleAsync(task); + task->sender.set_value(std::move(rle)); + delete task; + } + } + +public: + RleTaskScheduler() { + for (unsigned n = 0; n != _count; ++n) { + _threads.emplace_back([&, n] { run(n); }); + } + } + + ~RleTaskScheduler() { + for (auto& e : _q) + e.done(); + + for (auto& e : _threads) + e.join(); + } + + std::future async(RleTask *task) { + auto receiver = std::move(task->receiver); + auto i = _index++; + + for (unsigned n = 0; n != _count; ++n) { + if (_q[(i + n) % _count].try_push(task)) return std::move(receiver); + } -VRle generateStrokeInfoAsync(const SW_FT_Outline *outline, SW_FT_Stroker_LineCap cap, - SW_FT_Stroker_LineJoin join, - int width, int meterLimit, - SW_FT_Bool closed) + _q[i % _count].push(task); + + return std::move(receiver); + } + + std::future strokeRle(FTOutline *outline, + SW_FT_Stroker_LineCap cap, + SW_FT_Stroker_LineJoin join, + int width, + int meterLimit, + SW_FT_Bool closed) { + RleTask *task = new RleTask(); + task->stroke = true; + task->outline = outline; + task->cap = cap; + task->join = join; + task->width = width; + task->meterLimit = meterLimit; + task->closed = closed; + return async(task); + } + + std::future fillRle(FTOutline *outline) { + RleTask *task = new RleTask(); + task->stroke = false; + task->outline = outline; + return async(task); + } +}; + +static RleTaskScheduler raster_scheduler; + +static VRle generateRleAsync(RleTask *task) { - SW_FT_Stroker stroker; - SW_FT_Stroker_New(&stroker); + if (task->stroke) { + // for stroke generation + SW_FT_Stroker stroker; + SW_FT_Stroker_New(&stroker); - uint points,contors; - SW_FT_Outline strokeOutline = { 0, 0, nullptr, nullptr, nullptr, SW_FT_OUTLINE_NONE }; + uint points,contors; + SW_FT_Outline strokeOutline = { 0, 0, nullptr, nullptr, nullptr, SW_FT_OUTLINE_NONE }; - SW_FT_Stroker_Set(stroker, width, cap, join, meterLimit); - SW_FT_Stroker_ParseOutline(stroker, outline, !closed); - SW_FT_Stroker_GetCounts(stroker,&points, &contors); + SW_FT_Stroker_Set(stroker, task->width, task->cap, task->join, task->meterLimit); + SW_FT_Stroker_ParseOutline(stroker, &task->outline->ft, !task->closed); + SW_FT_Stroker_GetCounts(stroker,&points, &contors); - strokeOutline.points = (SW_FT_Vector *) calloc(points, sizeof(SW_FT_Vector)); - strokeOutline.tags = (char *) calloc(points, sizeof(char)); - strokeOutline.contours = (short *) calloc(contors, sizeof(short)); + strokeOutline.points = (SW_FT_Vector *) calloc(points, sizeof(SW_FT_Vector)); + strokeOutline.tags = (char *) calloc(points, sizeof(char)); + strokeOutline.contours = (short *) calloc(contors, sizeof(short)); - SW_FT_Stroker_Export(stroker, &strokeOutline); + SW_FT_Stroker_Export(stroker, &strokeOutline); - SW_FT_Stroker_Done(stroker); + SW_FT_Stroker_Done(stroker); - VRle rle = generateFillInfoAsync(&strokeOutline); + VRle rle; + SW_FT_Raster_Params params; - // cleanup the outline data. - free(strokeOutline.points); - free(strokeOutline.tags); - free(strokeOutline.contours); + params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ; + params.gray_spans = &rleGenerationCb; + params.user = &rle; + params.source = &strokeOutline; - return rle; -} + sw_ft_grays_raster.raster_render(nullptr, ¶ms); + // cleanup the outline data. + free(strokeOutline.points); + free(strokeOutline.tags); + free(strokeOutline.contours); + + return rle; + } else { + // fill generation + VRle rle; + SW_FT_Raster_Params params; + + params.flags = SW_FT_RASTER_FLAG_DIRECT | SW_FT_RASTER_FLAG_AA ; + params.gray_spans = &rleGenerationCb; + params.user = &rle; + params.source = &task->outline->ft; + + sw_ft_grays_raster.raster_render(nullptr, ¶ms); + + return rle; + } +} VRaster::VRaster() { @@ -231,7 +334,8 @@ FTOutline *VRaster::toFTOutline(const VPath &path) return outline; } -VRle VRaster::generateFillInfo(const FTOutline *outline, FillRule fillRule) +std::future +VRaster::generateFillInfo(FTOutline *outline, FillRule fillRule) { int fillRuleFlag = SW_FT_OUTLINE_NONE; switch (fillRule) { @@ -242,19 +346,21 @@ VRle VRaster::generateFillInfo(const FTOutline *outline, FillRule fillRule) fillRuleFlag = SW_FT_OUTLINE_NONE; break; } - FTOutline *outlineRef = const_cast(outline); - outlineRef->ft.flags = fillRuleFlag; - return generateFillInfoAsync(&outlineRef->ft); + + outline->ft.flags = fillRuleFlag; + + return std::move(raster_scheduler.fillRle(outline)); } -VRle VRaster::generateStrokeInfo(const FTOutline *outline, CapStyle cap, JoinStyle join, - float width, float meterLimit) +std::future +VRaster::generateStrokeInfo(FTOutline *outline, CapStyle cap, JoinStyle join, + float width, float meterLimit) { SW_FT_Stroker_LineCap ftCap; SW_FT_Stroker_LineJoin ftJoin; int ftWidth; int ftMeterLimit; - SW_FT_Bool ftbool = (SW_FT_Bool) outline->closed; + SW_FT_Bool ftclose = (SW_FT_Bool) outline->closed; // map strokeWidth to freetype. It uses as the radius of the pen not the diameter width = width/2.0; @@ -288,8 +394,8 @@ VRle VRaster::generateStrokeInfo(const FTOutline *outline, CapStyle cap, JoinSty break; } - return generateStrokeInfoAsync(&outline->ft, ftCap, ftJoin, - ftWidth, ftMeterLimit, ftbool); + return std::move(raster_scheduler.strokeRle(outline, ftCap, ftJoin, + ftWidth, ftMeterLimit, ftclose)); } V_END_NAMESPACE diff --git a/src/vector/vraster.h b/src/vector/vraster.h index 5b8d0ad..65baab6 100644 --- a/src/vector/vraster.h +++ b/src/vector/vraster.h @@ -25,9 +25,9 @@ public: static FTOutline *toFTOutline(const VPath &path); static void deleteFTOutline(FTOutline *); - VRle generateFillInfo(const FTOutline *, FillRule fillRule = FillRule::Winding); - VRle generateStrokeInfo(const FTOutline *, CapStyle cap, JoinStyle join, - float width, float meterLimit); + std::future generateFillInfo(FTOutline *, FillRule fillRule = FillRule::Winding); + std::future generateStrokeInfo(FTOutline *, CapStyle cap, JoinStyle join, + float width, float meterLimit); private: VRaster(); VRasterPrivate *d; -- 2.7.4