forked from enlightenment/efl
ector: move RLE shape/stroke computation to a pool of thread.
This has been a long standing plan for improving performance in rendering vector object. Depending on the test, you will get an improvement between 10 to 35% when rendering vector based object. We are still maintaining the Cairo backend as the default one at the moment due to a lack of result comparison tests between the two engine. Hopefully we should get that covered and we can all enjoy a backend that is 4 times faster by default.
This commit is contained in:
parent
560e5c8e0f
commit
e380ddb742
|
@ -13,16 +13,32 @@
|
|||
#include "ector_software_private.h"
|
||||
|
||||
typedef struct _Ector_Renderer_Software_Shape_Data Ector_Renderer_Software_Shape_Data;
|
||||
typedef struct _Ector_Software_Shape_Task Ector_Software_Shape_Task;
|
||||
|
||||
struct _Ector_Software_Shape_Task
|
||||
{
|
||||
Ector_Renderer_Software_Shape_Data *pd;
|
||||
|
||||
const Efl_Gfx_Path_Command *cmds;
|
||||
const double *pts;
|
||||
|
||||
Efl_Gfx_Fill_Rule fill_rule;
|
||||
};
|
||||
|
||||
struct _Ector_Renderer_Software_Shape_Data
|
||||
{
|
||||
Efl_Gfx_Shape_Public *public_shape;
|
||||
Efl_Gfx_Shape_Public *public_shape;
|
||||
|
||||
Ector_Software_Surface_Data *surface;
|
||||
Ector_Software_Surface_Data *surface;
|
||||
Ector_Renderer_Shape_Data *shape;
|
||||
Ector_Renderer_Data *base;
|
||||
Ector_Renderer_Data *base;
|
||||
|
||||
Shape_Rle_Data *shape_data;
|
||||
Shape_Rle_Data *outline_data;
|
||||
Shape_Rle_Data *shape_data;
|
||||
Shape_Rle_Data *outline_data;
|
||||
|
||||
Ector_Software_Shape_Task *task;
|
||||
|
||||
Eina_Bool done;
|
||||
};
|
||||
|
||||
typedef struct _Outline
|
||||
|
@ -511,70 +527,110 @@ _generate_shape_data(Ector_Renderer_Software_Shape_Data *pd)
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_update_rle(Eo *obj, Ector_Renderer_Software_Shape_Data *pd)
|
||||
static Ector_Software_Shape_Task *
|
||||
_need_update_rle(Eo *obj, Ector_Renderer_Software_Shape_Data *pd)
|
||||
{
|
||||
const Efl_Gfx_Path_Command *cmds = NULL;
|
||||
const double *pts = NULL;
|
||||
Eina_Bool close_path;
|
||||
Ector_Software_Shape_Task *r;
|
||||
const Efl_Gfx_Path_Command *cmds;
|
||||
const double *pts;
|
||||
Efl_Gfx_Fill_Rule fill_rule;
|
||||
Outline *outline, *dash_outline;
|
||||
|
||||
if (!pd->done && pd->task) return pd->task;
|
||||
|
||||
if (!_generate_stroke_data(pd) &&
|
||||
!_generate_shape_data(pd))
|
||||
return NULL;
|
||||
|
||||
efl_gfx_path_get(obj, &cmds, &pts);
|
||||
fill_rule = efl_gfx_shape_fill_rule_get(obj);
|
||||
if (cmds && (_generate_stroke_data(pd) || _generate_shape_data(pd)))
|
||||
|
||||
if (!cmds) return NULL;
|
||||
|
||||
r = pd->task;
|
||||
if (!r) r = malloc(sizeof (Ector_Software_Shape_Task));
|
||||
if (!r) return NULL;
|
||||
|
||||
r->pd = pd;
|
||||
r->cmds = cmds;
|
||||
r->pts = pts;
|
||||
r->fill_rule = fill_rule;
|
||||
|
||||
pd->done = EINA_FALSE;
|
||||
pd->task = r;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void
|
||||
_done_rle(void *data)
|
||||
{
|
||||
Ector_Software_Shape_Task *task = data;
|
||||
|
||||
task->pd->done = EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_update_rle(void *data, Ector_Software_Thread *thread)
|
||||
{
|
||||
Ector_Software_Shape_Task *task = data;
|
||||
Eina_Bool close_path;
|
||||
Outline *outline, *dash_outline;
|
||||
|
||||
outline = _outline_create();
|
||||
close_path = _generate_outline(task->cmds, task->pts, outline);
|
||||
if (task->fill_rule == EFL_GFX_FILL_RULE_ODD_EVEN)
|
||||
outline->ft_outline.flags = SW_FT_OUTLINE_EVEN_ODD_FILL;
|
||||
else
|
||||
outline->ft_outline.flags = SW_FT_OUTLINE_NONE; // default is winding fill
|
||||
|
||||
_outline_transform(outline, task->pd->base->m);
|
||||
|
||||
//shape data generation
|
||||
if (_generate_shape_data(task->pd))
|
||||
task->pd->shape_data = ector_software_rasterizer_generate_rle_data(thread,
|
||||
task->pd->surface->rasterizer,
|
||||
&outline->ft_outline);
|
||||
|
||||
//stroke data generation
|
||||
if (_generate_stroke_data(task->pd))
|
||||
{
|
||||
outline = _outline_create();
|
||||
close_path = _generate_outline(cmds, pts, outline);
|
||||
if (fill_rule == EFL_GFX_FILL_RULE_ODD_EVEN)
|
||||
outline->ft_outline.flags = SW_FT_OUTLINE_EVEN_ODD_FILL;
|
||||
else
|
||||
outline->ft_outline.flags = SW_FT_OUTLINE_NONE; // default is winding fill
|
||||
ector_software_rasterizer_stroke_set(thread, task->pd->surface->rasterizer,
|
||||
(task->pd->public_shape->stroke.width *
|
||||
task->pd->public_shape->stroke.scale),
|
||||
task->pd->public_shape->stroke.cap,
|
||||
task->pd->public_shape->stroke.join,
|
||||
task->pd->base->m);
|
||||
|
||||
_outline_transform(outline, pd->base->m);
|
||||
|
||||
//shape data generation
|
||||
if (_generate_shape_data(pd))
|
||||
pd->shape_data = ector_software_rasterizer_generate_rle_data(pd->surface->rasterizer,
|
||||
&outline->ft_outline);
|
||||
|
||||
//stroke data generation
|
||||
if ( _generate_stroke_data(pd))
|
||||
if (task->pd->public_shape->stroke.dash)
|
||||
{
|
||||
ector_software_rasterizer_stroke_set(pd->surface->rasterizer,
|
||||
(pd->public_shape->stroke.width *
|
||||
pd->public_shape->stroke.scale),
|
||||
pd->public_shape->stroke.cap,
|
||||
pd->public_shape->stroke.join,
|
||||
pd->base->m);
|
||||
|
||||
if (pd->public_shape->stroke.dash)
|
||||
{
|
||||
dash_outline = _outline_create();
|
||||
close_path = _generate_dashed_outline(cmds, pts, dash_outline,
|
||||
pd->public_shape->stroke.dash,
|
||||
pd->public_shape->stroke.dash_length);
|
||||
_outline_transform(dash_outline, pd->base->m);
|
||||
pd->outline_data = ector_software_rasterizer_generate_stroke_rle_data(pd->surface->rasterizer,
|
||||
&dash_outline->ft_outline,
|
||||
close_path);
|
||||
_outline_destroy(dash_outline);
|
||||
}
|
||||
else
|
||||
{
|
||||
pd->outline_data = ector_software_rasterizer_generate_stroke_rle_data(pd->surface->rasterizer,
|
||||
&outline->ft_outline,
|
||||
close_path);
|
||||
}
|
||||
dash_outline = _outline_create();
|
||||
close_path = _generate_dashed_outline(task->cmds, task->pts, dash_outline,
|
||||
task->pd->public_shape->stroke.dash,
|
||||
task->pd->public_shape->stroke.dash_length);
|
||||
_outline_transform(dash_outline, task->pd->base->m);
|
||||
task->pd->outline_data = ector_software_rasterizer_generate_stroke_rle_data(thread,
|
||||
task->pd->surface->rasterizer,
|
||||
&dash_outline->ft_outline,
|
||||
close_path);
|
||||
_outline_destroy(dash_outline);
|
||||
}
|
||||
else
|
||||
{
|
||||
task->pd->outline_data = ector_software_rasterizer_generate_stroke_rle_data(thread,
|
||||
task->pd->surface->rasterizer,
|
||||
&outline->ft_outline,
|
||||
close_path);
|
||||
}
|
||||
_outline_destroy(outline);
|
||||
}
|
||||
_outline_destroy(outline);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_software_shape_ector_renderer_prepare(Eo *obj,
|
||||
Ector_Renderer_Software_Shape_Data *pd)
|
||||
Ector_Renderer_Software_Shape_Data *pd)
|
||||
{
|
||||
Ector_Software_Shape_Task *task;
|
||||
|
||||
// FIXME: shouldn't this be part of the shape generic implementation?
|
||||
if (pd->shape->fill)
|
||||
ector_renderer_prepare(pd->shape->fill);
|
||||
|
@ -587,19 +643,25 @@ _ector_renderer_software_shape_ector_renderer_prepare(Eo *obj,
|
|||
if (!pd->surface)
|
||||
pd->surface = efl_data_xref(pd->base->surface, ECTOR_SOFTWARE_SURFACE_CLASS, obj);
|
||||
|
||||
// Asynchronously lazy build of the RLE data for this shape
|
||||
task = _need_update_rle(obj, pd);
|
||||
if (task) ector_software_schedule(_update_rle, _done_rle, task);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_ector_renderer_software_shape_ector_renderer_draw(Eo *obj,
|
||||
Ector_Renderer_Software_Shape_Data *pd,
|
||||
Efl_Gfx_Render_Op op, Eina_Array *clips,
|
||||
unsigned int mul_col)
|
||||
Ector_Renderer_Software_Shape_Data *pd,
|
||||
Efl_Gfx_Render_Op op, Eina_Array *clips,
|
||||
unsigned int mul_col)
|
||||
{
|
||||
Ector_Software_Shape_Task *task;
|
||||
int x, y;
|
||||
|
||||
// do lazy creation of rle
|
||||
_update_rle(obj, pd);
|
||||
// check if RLE data are ready
|
||||
task = _need_update_rle(obj, pd);
|
||||
if (task) ector_software_wait(_update_rle, _done_rle, task);
|
||||
|
||||
// adjust the offset
|
||||
x = pd->surface->x + (int)pd->base->origin.x;
|
||||
|
@ -684,10 +746,10 @@ static void
|
|||
_ector_renderer_software_shape_path_changed(void *data, const Efl_Event *event EINA_UNUSED)
|
||||
{
|
||||
Ector_Renderer_Software_Shape_Data *pd = data;
|
||||
|
||||
|
||||
if (pd->shape_data) ector_software_rasterizer_destroy_rle_data(pd->shape_data);
|
||||
if (pd->outline_data) ector_software_rasterizer_destroy_rle_data(pd->outline_data);
|
||||
|
||||
|
||||
pd->shape_data = NULL;
|
||||
pd->outline_data = NULL;
|
||||
}
|
||||
|
@ -698,6 +760,8 @@ _ector_renderer_software_shape_efl_object_constructor(Eo *obj, Ector_Renderer_So
|
|||
obj = efl_constructor(efl_super(obj, ECTOR_RENDERER_SOFTWARE_SHAPE_CLASS));
|
||||
if (!obj) return NULL;
|
||||
|
||||
pd->task = NULL;
|
||||
pd->done = EINA_FALSE;
|
||||
pd->public_shape = efl_data_xref(obj, EFL_GFX_SHAPE_MIXIN, obj);
|
||||
pd->shape = efl_data_xref(obj, ECTOR_RENDERER_SHAPE_MIXIN, obj);
|
||||
pd->base = efl_data_xref(obj, ECTOR_RENDERER_CLASS, obj);
|
||||
|
@ -711,10 +775,14 @@ _ector_renderer_software_shape_efl_object_destructor(Eo *obj, Ector_Renderer_Sof
|
|||
{
|
||||
// FIXME: As base class, destructor can't call destructor of mixin class.
|
||||
// Call explicit API to free shape data.
|
||||
if (!pd->done && pd->task)
|
||||
ector_software_wait(_update_rle, _done_rle, pd->task);
|
||||
|
||||
efl_gfx_path_reset(obj);
|
||||
|
||||
if (pd->shape_data) ector_software_rasterizer_destroy_rle_data(pd->shape_data);
|
||||
if (pd->outline_data) ector_software_rasterizer_destroy_rle_data(pd->outline_data);
|
||||
free(pd->task);
|
||||
|
||||
efl_data_xunref(pd->base->surface, pd->surface, obj);
|
||||
efl_data_xunref(obj, pd->shape, obj);
|
||||
|
|
|
@ -95,13 +95,9 @@ typedef struct _Span_Data
|
|||
|
||||
typedef struct _Software_Rasterizer
|
||||
{
|
||||
SW_FT_Raster raster;
|
||||
SW_FT_Stroker stroker;
|
||||
|
||||
Span_Data fill_data;
|
||||
Eina_Matrix3 *transform;
|
||||
Eina_Rectangle system_clip;
|
||||
|
||||
} Software_Rasterizer;
|
||||
|
||||
struct _Ector_Software_Surface_Data
|
||||
|
@ -114,9 +110,9 @@ struct _Ector_Software_Surface_Data
|
|||
|
||||
int ector_software_gradient_init(void);
|
||||
void ector_software_rasterizer_init(Software_Rasterizer *rasterizer);
|
||||
void ector_software_rasterizer_done(Software_Rasterizer *rasterizer);
|
||||
|
||||
void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, double width,
|
||||
void ector_software_rasterizer_stroke_set(Ector_Software_Thread *thread, Software_Rasterizer *rasterizer,
|
||||
double width,
|
||||
Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style, Eina_Matrix3 *m);
|
||||
|
||||
void ector_software_rasterizer_transform_set(Software_Rasterizer *rasterizer, Eina_Matrix3 *t);
|
||||
|
|
|
@ -300,14 +300,18 @@ _adjust_span_fill_methods(Span_Data *spdata)
|
|||
}
|
||||
}
|
||||
|
||||
void ector_software_rasterizer_init(Software_Rasterizer *rasterizer)
|
||||
void ector_software_thread_init(Ector_Software_Thread *thread)
|
||||
{
|
||||
// initialize the rasterizer and stroker
|
||||
sw_ft_grays_raster.raster_new(&rasterizer->raster);
|
||||
sw_ft_grays_raster.raster_new(&thread->raster);
|
||||
|
||||
SW_FT_Stroker_New(&rasterizer->stroker);
|
||||
SW_FT_Stroker_Set(rasterizer->stroker, 1<<6,SW_FT_STROKER_LINECAP_BUTT,SW_FT_STROKER_LINEJOIN_MITER,0);
|
||||
SW_FT_Stroker_New(&thread->stroker);
|
||||
SW_FT_Stroker_Set(thread->stroker, 1 << 6,
|
||||
SW_FT_STROKER_LINECAP_BUTT, SW_FT_STROKER_LINEJOIN_MITER, 0);
|
||||
}
|
||||
|
||||
void ector_software_rasterizer_init(Software_Rasterizer *rasterizer)
|
||||
{
|
||||
//initialize the span data.
|
||||
rasterizer->fill_data.clip.enabled = EINA_FALSE;
|
||||
rasterizer->fill_data.unclipped_blend = 0;
|
||||
|
@ -316,13 +320,14 @@ void ector_software_rasterizer_init(Software_Rasterizer *rasterizer)
|
|||
ector_software_gradient_init();
|
||||
}
|
||||
|
||||
void ector_software_rasterizer_done(Software_Rasterizer *rasterizer)
|
||||
void ector_software_thread_shutdown(Ector_Software_Thread *thread)
|
||||
{
|
||||
sw_ft_grays_raster.raster_done(rasterizer->raster);
|
||||
SW_FT_Stroker_Done(rasterizer->stroker);
|
||||
sw_ft_grays_raster.raster_done(thread->raster);
|
||||
SW_FT_Stroker_Done(thread->stroker);
|
||||
}
|
||||
|
||||
void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, double width,
|
||||
void ector_software_rasterizer_stroke_set(Ector_Software_Thread *thread,
|
||||
Software_Rasterizer *rasterizer EINA_UNUSED, double width,
|
||||
Efl_Gfx_Cap cap_style, Efl_Gfx_Join join_style,
|
||||
Eina_Matrix3 *m)
|
||||
{
|
||||
|
@ -366,7 +371,7 @@ void ector_software_rasterizer_stroke_set(Software_Rasterizer *rasterizer, doubl
|
|||
join = SW_FT_STROKER_LINEJOIN_MITER;
|
||||
break;
|
||||
}
|
||||
SW_FT_Stroker_Set(rasterizer->stroker, stroke_width, cap, join, 0);
|
||||
SW_FT_Stroker_Set(thread->stroker, stroke_width, cap, join, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -393,7 +398,9 @@ _rle_generation_cb( int count, const SW_FT_Span* spans,void *user)
|
|||
}
|
||||
|
||||
Shape_Rle_Data *
|
||||
ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline)
|
||||
ector_software_rasterizer_generate_rle_data(Ector_Software_Thread *thread,
|
||||
Software_Rasterizer *rasterizer EINA_UNUSED,
|
||||
SW_FT_Outline *outline)
|
||||
{
|
||||
int i, rle_size;
|
||||
int l = 0, t = 0, r = 0, b = 0;
|
||||
|
@ -406,7 +413,7 @@ ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_
|
|||
params.user = rle_data;
|
||||
params.source = outline;
|
||||
|
||||
sw_ft_grays_raster.raster_render(rasterizer->raster, ¶ms);
|
||||
sw_ft_grays_raster.raster_render(thread->raster, ¶ms);
|
||||
|
||||
// update RLE bounding box.
|
||||
span = rle_data->spans;
|
||||
|
@ -429,22 +436,25 @@ ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_
|
|||
}
|
||||
|
||||
Shape_Rle_Data *
|
||||
ector_software_rasterizer_generate_stroke_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath)
|
||||
ector_software_rasterizer_generate_stroke_rle_data(Ector_Software_Thread *thread,
|
||||
Software_Rasterizer *rasterizer,
|
||||
SW_FT_Outline *outline,
|
||||
Eina_Bool closePath)
|
||||
{
|
||||
uint32_t points,contors;
|
||||
Shape_Rle_Data *rle_data;
|
||||
SW_FT_Outline strokeOutline = { 0, 0, NULL, NULL, NULL, 0 };
|
||||
|
||||
SW_FT_Stroker_ParseOutline(rasterizer->stroker, outline, !closePath);
|
||||
SW_FT_Stroker_GetCounts(rasterizer->stroker,&points, &contors);
|
||||
SW_FT_Stroker_ParseOutline(thread->stroker, outline, !closePath);
|
||||
SW_FT_Stroker_GetCounts(thread->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));
|
||||
|
||||
SW_FT_Stroker_Export(rasterizer->stroker, &strokeOutline);
|
||||
SW_FT_Stroker_Export(thread->stroker, &strokeOutline);
|
||||
|
||||
rle_data = ector_software_rasterizer_generate_rle_data(rasterizer, &strokeOutline);
|
||||
rle_data = ector_software_rasterizer_generate_rle_data(thread, rasterizer, &strokeOutline);
|
||||
|
||||
// cleanup the outline data.
|
||||
free(strokeOutline.points);
|
||||
|
|
|
@ -90,6 +90,7 @@ _ector_software_init(void)
|
|||
|
||||
t = &ths[i];
|
||||
t->queue = eina_thread_queue_new();
|
||||
ector_software_thread_init(t);
|
||||
if (!eina_thread_create(&t->thread, EINA_THREAD_NORMAL, -1,
|
||||
_prepare_process, t))
|
||||
{
|
||||
|
@ -107,7 +108,11 @@ _ector_software_shutdown(void)
|
|||
|
||||
if (!--count_init) return ;
|
||||
|
||||
if (!ths) return ;
|
||||
if (!ths)
|
||||
{
|
||||
ector_software_thread_shutdown(&render_thread);
|
||||
return ;
|
||||
}
|
||||
|
||||
for (i = 0; i < cpu_core; i++)
|
||||
{
|
||||
|
@ -123,6 +128,7 @@ _ector_software_shutdown(void)
|
|||
|
||||
eina_thread_join(t->thread);
|
||||
eina_thread_queue_free(t->queue);
|
||||
ector_software_thread_shutdown(t);
|
||||
}
|
||||
|
||||
eina_thread_queue_free(render_queue);
|
||||
|
@ -220,7 +226,6 @@ _ector_software_surface_efl_object_constructor(Eo *obj, Ector_Software_Surface_D
|
|||
static void
|
||||
_ector_software_surface_efl_object_destructor(Eo *obj, Ector_Software_Surface_Data *pd)
|
||||
{
|
||||
ector_software_rasterizer_done(pd->rasterizer);
|
||||
efl_data_unref(obj, pd->rasterizer->fill_data.raster_buffer);
|
||||
free(pd->rasterizer);
|
||||
pd->rasterizer = NULL;
|
||||
|
|
Loading…
Reference in New Issue