ector: add ability to run task in another thread during preparation stage.
authorCedric Bail <cedric@osg.samsung.com>
Sun, 17 Sep 2017 00:43:18 +0000 (17:43 -0700)
committerCedric Bail <cedric@osg.samsung.com>
Sun, 17 Sep 2017 18:49:48 +0000 (11:49 -0700)
src/lib/ector/software/ector_software_private.h
src/lib/ector/software/ector_software_surface.c

index 459de01..a255fd3 100644 (file)
@@ -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
index 1631449..016b267 100644 (file)
 
 #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