sw_engine: optimize rasterizey by threading it.

Also, newly introduced render interfaces: preRender(), postRender(), flush()

Change-Id: If506fa27e3c7dbd89f6734cad4774c1d151b88aa
This commit is contained in:
Hermet Park 2020-06-24 17:10:50 +09:00
parent 36c76ca73c
commit 4156de72f1
13 changed files with 166 additions and 66 deletions

View file

@ -153,7 +153,7 @@ public:
virtual Result update() noexcept;
virtual Result update(Paint* paint) noexcept;
virtual Result draw(bool async = true) noexcept;
virtual Result sync() = 0;
virtual Result sync() noexcept;
_TVG_DECLARE_ACCESSOR(Scene);
_TVG_DECLARE_PRIVATE(Canvas);
@ -315,7 +315,6 @@ public:
~SwCanvas();
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
Result sync() noexcept override;
static std::unique_ptr<SwCanvas> gen() noexcept;
@ -337,9 +336,7 @@ public:
~GlCanvas();
//TODO: Gl Specific methods. Need gl backend configuration methods as well.
Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept;
Result sync() noexcept override;
static std::unique_ptr<GlCanvas> gen() noexcept;

View file

@ -53,10 +53,28 @@ bool GlRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
}
void GlRenderer::flush()
bool GlRenderer::flush()
{
GL_CHECK(glFinish());
mColorProgram->unload();
return true;
}
bool GlRenderer::preRender()
{
//TODO: called just before render()
return true;
}
bool GlRenderer::postRender()
{
//TODO: called just after render()
return true;
}

View file

@ -28,9 +28,11 @@ 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 postRender() override;
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
void flush();
bool flush() override;
bool clear() override;
uint32_t ref() override;
uint32_t unref() override;

View file

@ -17,8 +17,6 @@
#ifndef _TVG_SW_COMMON_H_
#define _TVG_SW_COMMON_H_
#include <future>
#include <thread>
#include "tvgCommon.h"
#if 0
@ -203,16 +201,6 @@ struct SwShape
};
struct SwTask
{
SwShape shape;
const Shape* sdata;
SwSize clip;
const Matrix* transform;
RenderUpdateFlag flags;
future<void> prepared;
};
static inline SwPoint TO_SWPOINT(const Point* pt)
{
return {SwCoord(pt->x * 64), SwCoord(pt->y * 64)};

View file

@ -17,6 +17,8 @@
#ifndef _TVG_SW_RENDERER_CPP_
#define _TVG_SW_RENDERER_CPP_
using namespace std;
#include "tvgSwCommon.h"
#include "tvgSwRenderer.h"
@ -24,19 +26,37 @@
/************************************************************************/
/* Internal Class Implementation */
/************************************************************************/
namespace tvg {
struct SwTask
{
SwShape shape;
const Shape* sdata;
SwSize clip;
const Matrix* transform;
RenderUpdateFlag flags;
future<void> progress;
};
}
static RenderInitializer renderInit;
/************************************************************************/
/* External Class Implementation */
/************************************************************************/
bool SwRenderer::clear()
SwRenderer::~SwRenderer()
{
return rasterClear(surface);
if (progress.valid()) progress.get();
}
bool SwRenderer::clear()
{
if (progress.valid()) return false;
return true;
}
bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h)
{
if (!buffer || stride == 0 || w == 0 || h == 0) return false;
@ -50,24 +70,64 @@ bool SwRenderer::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
}
bool SwRenderer::preRender()
{
//before we start rendering, we should finish all preparing tasks
while (prepareTasks.size() > 0) {
auto task = prepareTasks.front();
if (task->progress.valid()) task->progress.get();
prepareTasks.pop();
renderTasks.push(task);
}
return true;
}
bool SwRenderer::postRender()
{
auto asyncTask = [](SwRenderer* renderer) {
renderer->doRender();
};
progress = async(launch::async, asyncTask, this);
return true;
}
void SwRenderer::doRender()
{
rasterClear(surface);
while (renderTasks.size() > 0) {
auto task = renderTasks.front();
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);
renderTasks.pop();
}
}
bool SwRenderer::flush()
{
if (progress.valid()) {
progress.get();
return true;
}
return false;
}
bool SwRenderer::render(const Shape& sdata, void *data)
{
auto task = static_cast<SwTask*>(data);
if (!task) return false;
if (task->prepared.valid()) task->prepared.get();
uint8_t r, g, b, a;
if (auto fill = sdata.fill()) {
rasterGradientShape(surface, task->shape, fill->id());
} else {
sdata.fill(&r, &g, &b, &a);
if (a > 0) rasterSolidShape(surface, task->shape, r, g, b, a);
}
sdata.strokeColor(&r, &g, &b, &a);
if (a > 0) rasterStroke(surface, task->shape, r, g, b, a);
//Do Nothing
return true;
}
@ -77,7 +137,7 @@ bool SwRenderer::dispose(const Shape& sdata, void *data)
{
auto task = static_cast<SwTask*>(data);
if (!task) return true;
if (task->prepared.valid()) task->prepared.wait();
if (task->progress.valid()) task->progress.get();
shapeFree(task->shape);
free(task);
return true;
@ -93,7 +153,7 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
if (!task) return nullptr;
}
if (flags == RenderUpdateFlag::None || task->prepared.valid()) return task;
if (flags == RenderUpdateFlag::None || task->progress.valid()) return task;
task->sdata = &sdata;
task->clip = {static_cast<SwCoord>(surface.w), static_cast<SwCoord>(surface.h)};
@ -140,7 +200,8 @@ void* SwRenderer::prepare(const Shape& sdata, void* data, const RenderTransform*
shapeDelOutline(task->shape);
};
task->prepared = async((launch::async | launch::deferred), asyncTask, task);
prepareTasks.push(task);
task->progress = async((launch::async | launch::deferred), asyncTask, task);
return task;
}

View file

@ -17,16 +17,26 @@
#ifndef _TVG_SW_RENDERER_H_
#define _TVG_SW_RENDERER_H_
#include <queue>
#include <future>
#include <thread>
namespace tvg
{
struct SwTask;
class SwRenderer : public RenderMethod
{
public:
Surface surface;
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 postRender() override;
bool target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h);
bool clear() override;
bool flush() override;
uint32_t ref() override;
uint32_t unref() override;
@ -34,9 +44,18 @@ public:
static int init();
static int term();
void doRender(); //Internally used for threading
private:
Surface surface;
future<void> progress;
queue<SwTask*> prepareTasks;
queue<SwTask*> renderTasks;
SwRenderer(){};
~SwRenderer(){};
~SwRenderer();
};
}
#endif /* _TVG_SW_RENDERER_H_ */

View file

@ -568,6 +568,8 @@ void shapeFree(SwShape& shape)
shapeDelOutline(shape);
rleFree(shape.rle);
shapeDelFill(shape);
if (shape.stroke) {
rleFree(shape.strokeRle);
strokeFree(shape.stroke);

View file

@ -82,4 +82,15 @@ Result Canvas::update(Paint* paint) noexcept
return impl->update(paint);
}
Result Canvas::sync() noexcept
{
auto impl = pImpl.get();
if (!impl) return Result::MemoryCorruption;
if (impl->renderer->flush()) return Result::Success;
return Result::InsufficientCondition;
}
#endif /* _TVG_CANVAS_CPP_ */

View file

@ -52,6 +52,9 @@ struct Canvas::Impl
{
if (!renderer) return Result::InsufficientCondition;
//Clear render target before drawing
if (!renderer->clear()) return Result::InsufficientCondition;
for (auto paint : paints) {
if (paint->id() == PAINT_ID_SCENE) {
//We know renderer type, avoid dynamic_cast for performance.
@ -104,8 +107,7 @@ struct Canvas::Impl
{
if (!renderer) return Result::InsufficientCondition;
//Clear render target before drawing
if (!renderer->clear()) return Result::InsufficientCondition;
if (!renderer->preRender()) return Result::InsufficientCondition;
for(auto paint: paints) {
if (paint->id() == PAINT_ID_SCENE) {
@ -117,6 +119,9 @@ struct Canvas::Impl
if(!SHAPE_IMPL->render(*shape, *renderer)) return Result::InsufficientCondition;
}
}
if (!renderer->postRender()) return Result::InsufficientCondition;
return Result::Success;
}
};

View file

@ -57,16 +57,6 @@ Result GlCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
}
Result GlCanvas::sync() noexcept
{
auto renderer = dynamic_cast<GlRenderer*>(Canvas::pImpl.get()->renderer);
assert(renderer);
renderer->flush();
return Result::Success;
}
unique_ptr<GlCanvas> GlCanvas::gen() noexcept
{
auto canvas = unique_ptr<GlCanvas>(new GlCanvas);

View file

@ -53,8 +53,11 @@ public:
virtual ~RenderMethod() {}
virtual void* prepare(const Shape& shape, void* data, const RenderTransform* transform, RenderUpdateFlag flags) = 0;
virtual bool dispose(const Shape& shape, void *data) = 0;
virtual bool preRender() = 0;
virtual bool render(const Shape& shape, void *data) = 0;
virtual bool postRender() = 0;
virtual bool clear() = 0;
virtual bool flush() = 0;
virtual uint32_t ref() = 0;
virtual uint32_t unref() = 0;
};

View file

@ -45,6 +45,7 @@ SwCanvas::~SwCanvas()
{
}
Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept
{
//We know renderer type, avoid dynamic_cast for performance.
@ -57,12 +58,6 @@ Result SwCanvas::target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t
}
Result SwCanvas::sync() noexcept
{
return Result::Success;
}
unique_ptr<SwCanvas> SwCanvas::gen() noexcept
{
auto canvas = unique_ptr<SwCanvas>(new SwCanvas);

View file

@ -21,9 +21,17 @@ void tvgtest()
Eina_Bool anim_cb(void *data)
{
auto t = ecore_time_get();
//Explicitly clear all retained paint nodes.
t1 = ecore_time_get();
canvas->clear();
if (canvas->clear() != tvg::Result::Success)
{
//Probably, you missed sync() call before.
return ECORE_CALLBACK_RENEW;
}
t1 = t;
t2 = ecore_time_get();
for (int i = 0; i < COUNT; i++) {
@ -61,6 +69,11 @@ Eina_Bool anim_cb(void *data)
canvas->push(move(shape));
}
t3 = ecore_time_get();
//Draw Next frames
canvas->draw();
//Update Efl Canvas
Eo* img = (Eo*) data;
evas_object_image_pixels_dirty_set(img, EINA_TRUE);
@ -71,10 +84,6 @@ Eina_Bool anim_cb(void *data)
void render_cb(void* data, Eo* obj)
{
t3 = ecore_time_get();
//Draw Next frames
canvas->draw();
canvas->sync();
t4 = ecore_time_get();