summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVito Caputo <vcaputo@pengaru.com>2018-02-26 03:29:12 -0800
committerVito Caputo <vcaputo@pengaru.com>2018-02-26 05:18:26 -0800
commit6fabe39ba8841ffe392c9b659505e12edaff43d6 (patch)
tree7a1f94427e9046679042225f1bd779067edab785
parent1bf11626543c2b0dd10e8e0e4f3f75c99ea4f16c (diff)
rototiller,fb: swap dispatch with page flipping
For the sake of sdl_fb, move page flipping into the main thread and run module render dispatch from another thread instead. This eliminates the fb flipper thread, moving its functionality into fb_flip() which synchronously consumes and performs a single flip from the same queue as before - the function is verbatim the loop body of the flipper thread. Now main() calls fb_flip() in a loop where it previously dispatched pages for rendering. Rendering dispatch is now performed in a created thread. See the comment in fb.c for more explanation of this shuffle.
-rw-r--r--src/fb.c82
-rw-r--r--src/fb.h1
-rw-r--r--src/rototiller.c72
3 files changed, 93 insertions, 62 deletions
diff --git a/src/fb.c b/src/fb.c
index f398d43..7485800 100644
--- a/src/fb.c
+++ b/src/fb.c
@@ -37,6 +37,17 @@
* longer be a flipper thread.
*
* Let me know if you're aware of a better way with existing mainline drm!
+ *
+ *
+ * XXX: fb_new() used to create a thread which did the equivalent of fb_flip()
+ * continuously in a loop. This posed a problem for the sdl_fb backend, due to
+ * the need for event pumping in the page flip hook. SDL internally uses TLS
+ * and requires that the same thread which initialized SDL call the event
+ * functions. To satisfy this requirement, the body of the flipper thread loop
+ * has been moved to the fb_flip() function. Rototiller's main thread is
+ * expected to call this repeatedly, turning it effectively into the flipper
+ * thread. This required rototiller to move what was previously the main
+ * thread's duties - page rendering dispatch, to a separate thread.
*/
@@ -55,8 +66,6 @@ typedef struct fb_t {
const fb_ops_t *ops;
void *ops_context;
- pthread_t thread;
-
_fb_page_t *active_page; /* page currently displayed */
pthread_mutex_t ready_mutex;
@@ -80,38 +89,39 @@ typedef struct fb_t {
/* Consumes ready pages queued via fb_page_put(), submits them to drm to flip
* on vsync. Produces inactive pages from those replaced, making them
* available to fb_page_get(). */
-static void * fb_flipper_thread(void *_fb)
+int fb_flip(fb_t *fb)
{
- fb_t *fb = _fb;
-
- for (;;) {
- _fb_page_t *next_active_page;
- /* wait for a flip req, submit the req page for flip on vsync, wait for it to flip before making the
- * active page inactive/available, repeat.
- */
- pthread_mutex_lock(&fb->ready_mutex);
- while (!fb->ready_pages_head)
- pthread_cond_wait(&fb->ready_cond, &fb->ready_mutex);
-
- next_active_page = fb->ready_pages_head;
- fb->ready_pages_head = next_active_page->next;
- if (!fb->ready_pages_head)
- fb->ready_pages_tail = NULL;
- pthread_mutex_unlock(&fb->ready_mutex);
-
- /* submit the next active page for page flip on vsync, and wait for it. */
- pexit_if(fb->ops->page_flip(fb->ops_context, next_active_page->ops_page) < 0,
- "unable to flip page");
-
- /* now that we're displaying a new page, make the previously active one inactive so rendering can reuse it */
- pthread_mutex_lock(&fb->inactive_mutex);
- fb->active_page->next = fb->inactive_pages;
- fb->inactive_pages = fb->active_page;
- pthread_cond_signal(&fb->inactive_cond);
- pthread_mutex_unlock(&fb->inactive_mutex);
-
- fb->active_page = next_active_page;
- }
+ _fb_page_t *next_active_page;
+ int r;
+
+ /* wait for a flip req, submit the req page for flip on vsync, wait for it to flip before making the
+ * active page inactive/available, repeat.
+ */
+ pthread_mutex_lock(&fb->ready_mutex);
+ while (!fb->ready_pages_head)
+ pthread_cond_wait(&fb->ready_cond, &fb->ready_mutex);
+
+ next_active_page = fb->ready_pages_head;
+ fb->ready_pages_head = next_active_page->next;
+ if (!fb->ready_pages_head)
+ fb->ready_pages_tail = NULL;
+ pthread_mutex_unlock(&fb->ready_mutex);
+
+ /* submit the next active page for page flip on vsync, and wait for it. */
+ r = fb->ops->page_flip(fb->ops_context, next_active_page->ops_page);
+ if (r < 0) /* TODO: vet this: what happens to this page? */
+ return r;
+
+ /* now that we're displaying a new page, make the previously active one inactive so rendering can reuse it */
+ pthread_mutex_lock(&fb->inactive_mutex);
+ fb->active_page->next = fb->inactive_pages;
+ fb->inactive_pages = fb->active_page;
+ pthread_cond_signal(&fb->inactive_cond);
+ pthread_mutex_unlock(&fb->inactive_mutex);
+
+ fb->active_page = next_active_page;
+
+ return 0;
}
@@ -126,9 +136,6 @@ static int fb_acquire(fb_t *fb, _fb_page_t *page)
fb->active_page = page;
- /* start up the page flipper thread */
- pthread_create(&fb->thread, NULL, fb_flipper_thread, fb);
-
return 0;
}
@@ -136,9 +143,6 @@ static int fb_acquire(fb_t *fb, _fb_page_t *page)
/* release the fb, making the visible page inactive */
static void fb_release(fb_t *fb)
{
- pthread_cancel(fb->thread);
- pthread_join(fb->thread, NULL);
-
fb->ops->release(fb->ops_context);
fb->active_page->next = fb->inactive_pages;
fb->inactive_pages = fb->active_page;
diff --git a/src/fb.h b/src/fb.h
index 7743b82..f9d0b77 100644
--- a/src/fb.h
+++ b/src/fb.h
@@ -47,6 +47,7 @@ void fb_page_put(fb_t *fb, fb_page_t *page);
void fb_free(fb_t *fb);
void fb_get_put_pages_count(fb_t *fb, unsigned *count);
fb_t * fb_new(const fb_ops_t *ops, settings_t *settings, int n_pages);
+int fb_flip(fb_t *fb);
void fb_fragment_divide(fb_fragment_t *fragment, unsigned n_fragments, fb_fragment_t fragments[]);
int fb_fragment_slice_single(const fb_fragment_t *fragment, unsigned n_fragments, unsigned num, fb_fragment_t *res_fragment);
int fb_fragment_tile_single(const fb_fragment_t *fragment, unsigned tile_size, unsigned num, fb_fragment_t *res_fragment);
diff --git a/src/rototiller.c b/src/rototiller.c
index 79d0778..0fbc794 100644
--- a/src/rototiller.c
+++ b/src/rototiller.c
@@ -129,6 +129,7 @@ int parse_argv(int argc, const char *argv[], argv_t *res_args)
return 0;
}
+
typedef struct setup_t {
settings_t *module;
settings_t *video;
@@ -182,6 +183,7 @@ static int setup_video(settings_t *settings, setting_desc_t **next_setting)
return -EINVAL;
}
+
/* select module if not yet selected, then setup the module. */
static int setup_module(settings_t *settings, setting_desc_t **next_setting)
{
@@ -223,6 +225,7 @@ static int setup_module(settings_t *settings, setting_desc_t **next_setting)
return 0;
}
+
/* turn args into settings, automatically applying defaults if appropriate, or interactively if appropriate. */
/* returns negative value on error, 0 when settings unchanged from args, 1 when changed */
static int setup_from_args(argv_t *args, int defaults, setup_t *res_setup)
@@ -321,6 +324,30 @@ static int print_help(void)
}
+typedef struct rototiller_t {
+ rototiller_module_t *module;
+ void *module_context;
+ threads_t *threads;
+ pthread_t thread;
+ fb_t *fb;
+} rototiller_t;
+
+void * rototiller_thread(void *_rt)
+{
+ rototiller_t *rt = _rt;
+
+ for (;;) {
+ fb_page_t *page;
+
+ page = fb_page_get(rt->fb);
+ module_render_page(rt->module, rt->module_context, rt->threads, page);
+ fb_page_put(rt->fb, page);
+ }
+
+ return NULL;
+}
+
+
/* When run with partial/no arguments, if stdin is a tty, enter an interactive setup.
* If stdin is not a tty, or if --defaults is supplied in argv, default settings are used.
* If any changes to the settings occur in the course of execution, either interactively or
@@ -329,13 +356,10 @@ static int print_help(void)
*/
int main(int argc, const char *argv[])
{
- argv_t args = {};
- setup_t setup = {};
- void *context = NULL;
- rototiller_module_t *module;
- threads_t *threads;
- fb_t *fb;
- int r;
+ rototiller_t rototiller = {};
+ setup_t setup = {};
+ argv_t args = {};
+ int r;
exit_if(parse_argv(argc, argv, &args) < 0,
"unable to process arguments");
@@ -349,38 +373,40 @@ int main(int argc, const char *argv[])
exit_if(r && print_setup_as_args(&setup) < 0,
"unable to print setup");
- exit_if(!(module = module_lookup(settings_get_key(setup.module, 0))),
+ exit_if(!(rototiller.module = module_lookup(settings_get_key(setup.module, 0))),
"unable to lookup module from settings \"%s\"", settings_get_key(setup.module, 0));
- exit_if(!(fb = fb_new(fb_ops, setup.video, NUM_FB_PAGES)),
+ exit_if(!(rototiller.fb = fb_new(fb_ops, setup.video, NUM_FB_PAGES)),
"unable to create fb");
exit_if(!fps_setup(),
"unable to setup fps counter");
- exit_if(module->create_context &&
- !(context = module->create_context()),
+ exit_if(rototiller.module->create_context &&
+ !(rototiller.module_context = rototiller.module->create_context()),
"unable to create module context");
- pexit_if(!(threads = threads_create()),
- "unable to create threads");
+ pexit_if(!(rototiller.threads = threads_create()),
+ "unable to create rendering threads");
- for (;;) {
- fb_page_t *page;
+ pexit_if(pthread_create(&rototiller.thread, NULL, rototiller_thread, &rototiller) != 0,
+ "unable to create dispatch thread");
- fps_print(fb);
+ for (;;) {
+ if (fb_flip(rototiller.fb) < 0)
+ break;
- page = fb_page_get(fb);
- module_render_page(module, context, threads, page);
- fb_page_put(fb, page);
+ fps_print(rototiller.fb);
}
- threads_destroy(threads);
+ pthread_cancel(rototiller.thread);
+ pthread_join(rototiller.thread, NULL);
+ threads_destroy(rototiller.threads);
- if (context)
- module->destroy_context(context);
+ if (rototiller.module_context)
+ rototiller.module->destroy_context(rototiller.module_context);
- fb_free(fb);
+ fb_free(rototiller.fb);
return EXIT_SUCCESS;
}
© All Rights Reserved