From dd297854d13832b275f9d709cdbf0b2ff359e0a8 Mon Sep 17 00:00:00 2001 From: Cedric Bail Date: Sat, 16 Sep 2017 17:43:18 -0700 Subject: [PATCH] ector: add ability to run task in another thread during preparation stage. --- src/lib/ector/software/ector_software_private.h | 23 ++- src/lib/ector/software/ector_software_surface.c | 183 ++++++++++++++++++++++++ 2 files changed, 203 insertions(+), 3 deletions(-) diff --git a/src/lib/ector/software/ector_software_private.h b/src/lib/ector/software/ector_software_private.h index 459de01..a255fd3 100644 --- a/src/lib/ector/software/ector_software_private.h +++ b/src/lib/ector/software/ector_software_private.h @@ -7,6 +7,16 @@ #include "../ector_private.h" typedef struct _Ector_Software_Surface_Data Ector_Software_Surface_Data; +typedef struct _Ector_Software_Thread Ector_Software_Thread; + +struct _Ector_Software_Thread +{ + Eina_Thread_Queue *queue; + Eina_Thread thread; + + SW_FT_Raster raster; + SW_FT_Stroker stroker; +}; // Gradient related structure typedef struct _Software_Gradient_Linear_Data @@ -118,19 +128,26 @@ void ector_software_rasterizer_clip_shape_set(Software_Rasterizer *rasterizer, S -Shape_Rle_Data * ector_software_rasterizer_generate_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline); -Shape_Rle_Data * ector_software_rasterizer_generate_stroke_rle_data(Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath); +Shape_Rle_Data * ector_software_rasterizer_generate_rle_data(Ector_Software_Thread *thread, Software_Rasterizer *rasterizer, SW_FT_Outline *outline); +Shape_Rle_Data * ector_software_rasterizer_generate_stroke_rle_data(Ector_Software_Thread *thread, Software_Rasterizer *rasterizer, SW_FT_Outline *outline, Eina_Bool closePath); void ector_software_rasterizer_draw_rle_data(Software_Rasterizer *rasterizer, int x, int y, uint32_t mul_col, Efl_Gfx_Render_Op op, Shape_Rle_Data* rle); void ector_software_rasterizer_destroy_rle_data(Shape_Rle_Data *rle); - // Gradient Api void update_color_table(Ector_Renderer_Software_Gradient_Data *gdata); void destroy_color_table(Ector_Renderer_Software_Gradient_Data *gdata); void fetch_linear_gradient(uint32_t *buffer, Span_Data *data, int y, int x, int length); void fetch_radial_gradient(uint32_t *buffer, Span_Data *data, int y, int x, int length); +void ector_software_thread_init(Ector_Software_Thread *thread); +void ector_software_thread_shutdown(Ector_Software_Thread *thread); + +typedef void (*Ector_Thread_Worker_Cb)(void *data, Ector_Software_Thread *thread); + +void ector_software_wait(Ector_Thread_Worker_Cb cb, Eina_Free_Cb done, void *data); +void ector_software_schedule(Ector_Thread_Worker_Cb cb, Eina_Free_Cb done, void *data); + #endif diff --git a/src/lib/ector/software/ector_software_surface.c b/src/lib/ector/software/ector_software_surface.c index 1631449..016b267 100644 --- a/src/lib/ector/software/ector_software_surface.c +++ b/src/lib/ector/software/ector_software_surface.c @@ -10,6 +10,185 @@ #define MY_CLASS ECTOR_SOFTWARE_SURFACE_CLASS +typedef struct _Ector_Software_Task Ector_Software_Task; + +struct _Ector_Software_Task +{ + Eina_Thread_Queue_Msg member; + + Ector_Thread_Worker_Cb cb; + Eina_Free_Cb done; + void *data; +}; + +static int count_init = 0; +static unsigned int current = 0; +static unsigned int cpu_core = 0; +static Ector_Software_Thread *ths = NULL; +static Eina_Thread_Queue *render_queue = NULL; +static Ector_Software_Thread render_thread; + +static void * +_prepare_process(void *data, Eina_Thread t) +{ + Ector_Software_Thread *th = data; + + eina_thread_name_set(t, "Ector Preparing Thread"); + do + { + Ector_Software_Task *task, todo; + void *ref; + + task = eina_thread_queue_wait(th->queue, &ref); + + if (!task) break ; + todo.cb = task->cb; + todo.data = task->data; + todo.done = task->done; + + eina_thread_queue_wait_done(th->queue, ref); + + if (!todo.cb) break ; + + todo.cb(todo.data, th); + + task = eina_thread_queue_send(render_queue, sizeof (Ector_Software_Task), &ref); + task->cb = todo.cb; + task->data = todo.data; + task->done = todo.done; + eina_thread_queue_send_done(render_queue, ref); + } + while (1); + + return th; +} + + +static void +_ector_software_init(void) +{ + int cpu, i; + + if (count_init++) return ; + + cpu = eina_cpu_count() - 1; + if (cpu < 1) + { + render_thread.queue = NULL; + ector_software_thread_init(&render_thread); + return ; + } + cpu = cpu > 8 ? 8 : cpu; + cpu_core = cpu; + + render_queue = eina_thread_queue_new(); + + ths = malloc(sizeof(Ector_Software_Thread) * cpu); + for (i = 0; i < cpu; i++) + { + Ector_Software_Thread *t; + + t = &ths[i]; + t->queue = eina_thread_queue_new(); + if (!eina_thread_create(&t->thread, EINA_THREAD_NORMAL, -1, + _prepare_process, t)) + { + eina_thread_queue_free(t->queue); + t->queue = NULL; + } + } +} + +static void +_ector_software_shutdown(void) +{ + Ector_Software_Thread *t; + unsigned int i; + + if (!--count_init) return ; + + if (!ths) return ; + + for (i = 0; i < cpu_core; i++) + { + Ector_Software_Task *task; + void *ref; + + t = &ths[i]; + + task = eina_thread_queue_send(t->queue, sizeof (Ector_Software_Task), &ref); + task->cb = NULL; + task->data = NULL; + eina_thread_queue_send_done(t->queue, ref); + + eina_thread_join(t->thread); + eina_thread_queue_free(t->queue); + } + + eina_thread_queue_free(render_queue); + render_queue = NULL; + + free(ths); + ths = NULL; +} + +void +ector_software_schedule(Ector_Thread_Worker_Cb cb, Eina_Free_Cb done, void *data) +{ + Ector_Software_Thread *t; + Ector_Software_Task *task; + void *ref; + + // Not enough CPU, doing it inline in the rendering thread + if (!ths) return ; + + t = &ths[current]; + current = (current + 1) % cpu_core; + + task = eina_thread_queue_send(t->queue, sizeof (Ector_Software_Task), &ref); + task->cb = cb; + task->done = done; + task->data = data; + eina_thread_queue_send_done(t->queue, ref); +} + +// Do not call this function if the done function has already called +void +ector_software_wait(Ector_Thread_Worker_Cb cb, Eina_Free_Cb done, void *data) +{ + Ector_Software_Task *task, covering; + + // First handle case with just inlined prepare code call inside the rendering thread + if (!ths) + { + render_thread.thread = eina_thread_self(); + cb(data, &render_thread); + done(data); + + return ; + } + + // We don't know which task is going to be done first, so + // we iterate until we find ourself back and trigger all + // the done call along the way. + do + { + void *ref; + + task = eina_thread_queue_wait(render_queue, &ref); + if (!task) break; + covering.cb = task->cb; + covering.done = task->done; + covering.data = task->data; + eina_thread_queue_wait_done(render_queue, ref); + + covering.done(covering.data); + } + while (covering.cb != cb || + covering.done != done || + covering.data != data); +} + static Ector_Renderer * _ector_software_surface_ector_surface_renderer_factory_new(Eo *obj, Ector_Software_Surface_Data *pd EINA_UNUSED, @@ -29,6 +208,8 @@ _ector_software_surface_ector_surface_renderer_factory_new(Eo *obj, static Eo * _ector_software_surface_efl_object_constructor(Eo *obj, Ector_Software_Surface_Data *pd) { + _ector_software_init(); + obj = efl_constructor(efl_super(obj, MY_CLASS)); pd->rasterizer = (Software_Rasterizer *) calloc(1, sizeof(Software_Rasterizer)); ector_software_rasterizer_init(pd->rasterizer); @@ -44,6 +225,8 @@ _ector_software_surface_efl_object_destructor(Eo *obj, Ector_Software_Surface_Da free(pd->rasterizer); pd->rasterizer = NULL; efl_destructor(efl_super(obj, ECTOR_SOFTWARE_SURFACE_CLASS)); + + _ector_software_shutdown(); } static void -- 2.7.4