From c8bc0a91d98fe5a4e984c84620baf690b1d6032e Mon Sep 17 00:00:00 2001 From: Hermet Park Date: Wed, 26 Aug 2020 16:39:10 +0900 Subject: [PATCH] sw_engine: applied async rasterizing based on task scheduler. Now, we have 2 points for asynchronous behaviors. 1. update shapes: Each shape update will be performed by async when you push shape to canvas. Meaning, if you have time gap between update and rendering in process main-loop, you can have a benefit by this. 2. rasterization by canvas: Canvas.draw() will be performed asynchnously until you call canvas.sync(); Meaing, if you can trigger tvg rendering eariler than composition time. You can have a benefit by this. If these 1, 2 points might not work for your program, You can just toggle off async by setting threads number zero at initialization. Or if you could apply either point of them for your program, It might be good for performance. But the best approach is to make both async properly. Though this might need to fine-grained tuning integration between your program & tvg, You could achieve the best peformance by parallelzing tasks as possible without any jobs delaying. Change-Id: I04f9a61ebb426fd897624f5b24c83841737e6b5b --- src/lib/sw_engine/tvgSwCommon.h | 10 +- src/lib/sw_engine/tvgSwRenderer.cpp | 195 ++++++++++++++++++---------- src/lib/sw_engine/tvgSwRenderer.h | 8 +- src/lib/tvgTaskScheduler.h | 5 + 4 files changed, 141 insertions(+), 77 deletions(-) diff --git a/src/lib/sw_engine/tvgSwCommon.h b/src/lib/sw_engine/tvgSwCommon.h index a600cb9d..256c4bbe 100644 --- a/src/lib/sw_engine/tvgSwCommon.h +++ b/src/lib/sw_engine/tvgSwCommon.h @@ -203,11 +203,11 @@ struct SwFill struct SwShape { - SwOutline* outline; - SwStroke* stroke; - SwFill* fill; - SwRleData* rle; - SwRleData* strokeRle; + SwOutline* outline = nullptr; + SwStroke* stroke = nullptr; + SwFill* fill = nullptr; + SwRleData* rle = nullptr; + SwRleData* strokeRle = nullptr; SwBBox bbox; bool rect; //Fast Track: Othogonal rectangle? diff --git a/src/lib/sw_engine/tvgSwRenderer.cpp b/src/lib/sw_engine/tvgSwRenderer.cpp index 28ddf639..0d77d386 100644 --- a/src/lib/sw_engine/tvgSwRenderer.cpp +++ b/src/lib/sw_engine/tvgSwRenderer.cpp @@ -27,6 +27,63 @@ /************************************************************************/ static RenderInitializer renderInit; +struct SwTask : Task +{ + SwShape shape; + const Shape* sdata = nullptr; + Matrix* transform = nullptr; + SwSurface* surface = nullptr; + RenderUpdateFlag flags = RenderUpdateFlag::None; + + void run() override + { + //Valid Stroking? + uint8_t strokeAlpha = 0; + auto strokeWidth = sdata->strokeWidth(); + if (strokeWidth > FLT_EPSILON) { + sdata->strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); + } + + SwSize clip = {static_cast(surface->w), static_cast(surface->h)}; + + //Shape + if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) { + shapeReset(&shape); + uint8_t alpha = 0; + sdata->fill(nullptr, nullptr, nullptr, &alpha); + bool renderShape = (alpha > 0 || sdata->fill()); + if (renderShape || strokeAlpha) { + if (!shapePrepare(&shape, sdata, clip, transform)) return; + if (renderShape) { + auto antiAlias = (strokeAlpha > 0 && strokeWidth >= 2) ? false : true; + if (!shapeGenRle(&shape, sdata, clip, antiAlias)) return; + } + } + } + //Fill + if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) { + auto fill = sdata->fill(); + if (fill) { + auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; + if (ctable) shapeResetFill(&shape); + if (!shapeGenFillColors(&shape, fill, transform, surface, ctable)) return; + } else { + shapeDelFill(&shape); + } + } + //Stroke + if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { + if (strokeAlpha > 0) { + shapeResetStroke(&shape, sdata, transform); + if (!shapeGenStrokeRle(&shape, sdata, transform, clip)) return; + } else { + shapeDelStroke(&shape); + } + } + shapeDelOutline(&shape); + } +}; + /************************************************************************/ /* External Class Implementation */ @@ -34,10 +91,30 @@ static RenderInitializer renderInit; SwRenderer::~SwRenderer() { + flush(); if (surface) delete(surface); } +bool SwRenderer::clear() +{ + if (this->valid() || tasks.size() > 0) return false; + + return flush(); +} + + +bool SwRenderer::flush() +{ + this->get(); //complete rendering + + for (auto task : tasks) task->get(); + tasks.clear(); + + return true; +} + + bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs) { if (!buffer || stride == 0 || w == 0 || h == 0) return false; @@ -59,98 +136,76 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t bool SwRenderer::preRender() { - return rasterClear(surface); -} + //before we start rendering, we should finish all preparing tasks + for (auto task : tasks) task->get(); - -bool SwRenderer::render(const Shape& sdata, TVG_UNUSED void *data) -{ - auto shape = static_cast(data); - if (!shape) return false; - - uint8_t r, g, b, a; - - if (auto fill = sdata.fill()) { - rasterGradientShape(surface, shape, fill->id()); - } else{ - sdata.fill(&r, &g, &b, &a); - if (a > 0) rasterSolidShape(surface, shape, r, g, b, a); - } - sdata.strokeColor(&r, &g, &b, &a); - if (a > 0) rasterStroke(surface, shape, r, g, b, a); + TaskScheduler::request(this); return true; } +void SwRenderer::run() +{ + rasterClear(surface); + + for (auto task : tasks) { + uint8_t r, g, b, a; + if (auto fill = task->sdata->fill()) { + rasterGradientShape(surface, &task->shape, fill->id()); + } else{ + task->sdata->fill(&r, &g, &b, &a); + if (a > 0) rasterSolidShape(surface, &task->shape, r, g, b, a); + } + task->sdata->strokeColor(&r, &g, &b, &a); + if (a > 0) rasterStroke(surface, &task->shape, r, g, b, a); + } + tasks.clear(); +}; + + bool SwRenderer::dispose(TVG_UNUSED const Shape& sdata, void *data) { - auto shape = static_cast(data); - if (!shape) return false; - shapeFree(shape); + auto task = static_cast(data); + if (!task) return true; + + task->get(); + shapeFree(&task->shape); + if (task->transform) free(task->transform); + delete(task); + return true; } void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform* transform, RenderUpdateFlag flags) { - //prepare shape data - auto shape = static_cast(data); - if (!shape) { - shape = static_cast(calloc(1, sizeof(SwShape))); - if (!shape) return nullptr; + //prepare task + auto task = static_cast(data); + if (!task) { + task = new SwTask; + if (!task) return nullptr; } - if (flags == RenderUpdateFlag::None) return shape; + if (flags == RenderUpdateFlag::None || task->valid()) return task; - SwSize clip = {static_cast(surface->w), static_cast(surface->h)}; + task->sdata = &sdata; - //Valid Stroking? - uint8_t strokeAlpha = 0; - auto strokeWidth = sdata.strokeWidth(); - if (strokeWidth > FLT_EPSILON) { - sdata.strokeColor(nullptr, nullptr, nullptr, &strokeAlpha); + if (transform) { + if (!task->transform) task->transform = static_cast(malloc(sizeof(Matrix))); + *task->transform = transform->m; + } else { + if (task->transform) free(task->transform); + task->transform = nullptr; } - const Matrix* matrix = (transform ? &transform->m : nullptr); + task->surface = surface; + task->flags = flags; - //Shape - if (flags & (RenderUpdateFlag::Path | RenderUpdateFlag::Transform)) { - shapeReset(shape); - uint8_t alpha = 0; - sdata.fill(nullptr, nullptr, nullptr, &alpha); - bool renderShape = (alpha > 0 || sdata.fill()); - if (renderShape || strokeAlpha) { - if (!shapePrepare(shape, &sdata, clip, matrix)) return shape; - if (renderShape) { - auto antiAlias = (strokeAlpha > 0 && strokeWidth >= 2) ? false : true; - if (!shapeGenRle(shape, &sdata, clip, antiAlias)) return shape; - } - } - } - //Fill - if (flags & (RenderUpdateFlag::Gradient | RenderUpdateFlag::Transform)) { - auto fill = sdata.fill(); - if (fill) { - auto ctable = (flags & RenderUpdateFlag::Gradient) ? true : false; - if (ctable) shapeResetFill(shape); - if (!shapeGenFillColors(shape, fill, matrix, surface, ctable)) return shape; - } else { - shapeDelFill(shape); - } - } - //Stroke - if (flags & (RenderUpdateFlag::Stroke | RenderUpdateFlag::Transform)) { - if (strokeAlpha > 0) { - shapeResetStroke(shape, &sdata, matrix); - if (!shapeGenStrokeRle(shape, &sdata, matrix, clip)) return shape; - } else { - shapeDelStroke(shape); - } - } - shapeDelOutline(shape); + tasks.push_back(task); + TaskScheduler::request(task); - return shape; + return task; } diff --git a/src/lib/sw_engine/tvgSwRenderer.h b/src/lib/sw_engine/tvgSwRenderer.h index 2185f344..67d2f159 100644 --- a/src/lib/sw_engine/tvgSwRenderer.h +++ b/src/lib/sw_engine/tvgSwRenderer.h @@ -23,20 +23,23 @@ #define _TVG_SW_RENDERER_H_ struct SwSurface; +struct SwTask; namespace tvg { -class SwRenderer : public RenderMethod +class SwRenderer : public RenderMethod, public Task { public: void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) override; bool dispose(const Shape& shape, void *data) override; bool preRender() override; - bool render(const Shape& shape, void *data) override; bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h, uint32_t cs); + bool clear() override; + bool flush() override; uint32_t ref() override; uint32_t unref() override; + void run() override; static SwRenderer* inst(); static int init(); @@ -44,6 +47,7 @@ public: private: SwSurface* surface = nullptr; + vector tasks; SwRenderer(){}; ~SwRenderer(); diff --git a/src/lib/tvgTaskScheduler.h b/src/lib/tvgTaskScheduler.h index 1b1cc128..31d1ee1a 100644 --- a/src/lib/tvgTaskScheduler.h +++ b/src/lib/tvgTaskScheduler.h @@ -43,6 +43,11 @@ public: } } + bool valid() + { + return receiver.valid(); + } + protected: virtual void run() = 0;