#include #include #include #include #include #include #include #include #include #include #include #include "drm_fb.h" #include "drmsetup.h" #include "fb.h" #include "fps.h" #include "rototiller.h" #include "threads.h" #include "util.h" /* Copyright (C) 2016 Vito Caputo */ #define NUM_FB_PAGES 3 /* ^ By triple-buffering, we can have a page tied up being displayed, another * tied up submitted and waiting for vsync, and still not block on getting * another page so we can begin rendering another frame before vsync. With * just two pages we end up twiddling thumbs until the vsync arrives. */ extern fb_ops_t drm_fb_ops; extern rototiller_module_t julia_module; extern rototiller_module_t plasma_module; extern rototiller_module_t roto32_module; extern rototiller_module_t roto64_module; extern rototiller_module_t ray_module; extern rototiller_module_t sparkler_module; extern rototiller_module_t stars_module; static rototiller_module_t *modules[] = { &roto32_module, &roto64_module, &ray_module, &sparkler_module, &stars_module, &plasma_module, &julia_module, }; static void module_select(int *module) { int i; printf("\nModules\n"); for (i = 0; i < nelems(modules); i++) { printf(" %i: %s - %s\n", i, modules[i]->name, modules[i]->description); } ask_num(module, nelems(modules) - 1, "Select module", 0); } static void module_render_page_threaded(rototiller_module_t *module, void *context, threads_t *threads, fb_page_t *page) { rototiller_fragmenter_t fragmenter; module->prepare_frame(context, threads_num_threads(threads), &page->fragment, &fragmenter); threads_frame_submit(threads, &page->fragment, fragmenter, module->render_fragment, context); threads_wait_idle(threads); } static void module_render_page(rototiller_module_t *module, void *context, threads_t *threads, fb_page_t *page) { if (module->prepare_frame) module_render_page_threaded(module, context, threads, page); else module->render_fragment(context, &page->fragment); if (module->finish_frame) module->finish_frame(context, &page->fragment); } typedef struct argv_t { const char *module; const char *video; unsigned defaults:1; unsigned help:1; } argv_t; /* * ./rototiller --video=drm,dev=/dev/dri/card3,connector=VGA-1,mode=640x480@60 * ./rototiller --video=sdl,size=640x480 * ./rototiller --module=roto,foo=bar,module=settings * ./rototiller --defaults */ int parse_argv(int argc, const char *argv[], argv_t *res_args) { int i; assert(argc > 0); assert(argv); assert(res_args); /* this is intentionally being kept very simple, no new dependencies like getopt. */ for (i = 1; i < argc; i++) { if (!strncmp("--video=", argv[i], 8)) { res_args->video = &argv[i][8]; } else if (!strncmp("--module=", argv[i], 9)) { res_args->module = &argv[i][9]; } else if (!strcmp("--defaults", argv[i])) { res_args->defaults = 1; } else if (!strcmp("--help", argv[i])) { res_args->help = 1; } else { return -EINVAL; } } return 0; } int main(int argc, const char *argv[]) { int drm_fd; drmModeModeInfoPtr drm_mode; uint32_t drm_crtc_id; uint32_t drm_connector_id; threads_t *threads; int module; fb_t *fb; void *context = NULL; drm_fb_t *drm_fb; drm_setup(&drm_fd, &drm_crtc_id, &drm_connector_id, &drm_mode); module_select(&module); pexit_if(!(drm_fb = drm_fb_new(drm_fd, drm_crtc_id, &drm_connector_id, 1, drm_mode)), "unable to create drm fb backend"); pexit_if(!(fb = fb_new(&drm_fb_ops, drm_fb, NUM_FB_PAGES)), "unable to create fb frontend"); pexit_if(!fps_setup(), "unable to setup fps counter"); exit_if(modules[module]->create_context && !(context = modules[module]->create_context()), "unable to create module context"); pexit_if(!(threads = threads_create()), "unable to create threads"); for (;;) { fb_page_t *page; fps_print(fb); page = fb_page_get(fb); module_render_page(modules[module], context, threads, page); fb_page_put(fb, page); } threads_destroy(threads); if (context) modules[module]->destroy_context(context); fb_free(fb); close(drm_fd); return EXIT_SUCCESS; }