From a59229c5513e73348c87bcfc5cc4b39a31012437 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Thu, 28 Apr 2022 01:46:10 -0700 Subject: til_fb: introduce a fragment texture source Idea here is to provide texture sources for obtaining pixel colors at the til_fb_put_pixel/fill drawing API, making it possible for at least overlayable modules to serve as mask/stencil operators where their drawn areas are populated by the contents of another fragment produced dynamically, potentially by other modules altogether. This commit adds a texture=modulename option to the compose module for specifying if a texture should be used when compositing, excepting and defaulting to "none" for disabling texturing. A future commit should expand this compose option to accept a potential list of modules for composing the texture in the same way as the main layers= list functions. Something this all immediately makes clear is the need for a better settings syntax, probably in the form of all module setting specifiers optionally being followed by a squence of settings, with support for escaping to handle nested situations. --- src/drm_fb.c | 16 ++++--- src/modules/blinds/blinds.c | 14 ++++-- src/modules/compose/compose.c | 100 +++++++++++++++++++++++++++++++++++++++++- src/modules/montage/montage.c | 25 ++++++----- src/sdl_fb.c | 16 ++++--- src/til_fb.c | 50 +++++++++++---------- src/til_fb.h | 75 +++++++++++++++++++++++++------ 7 files changed, 229 insertions(+), 67 deletions(-) diff --git a/src/drm_fb.c b/src/drm_fb.c index 927f46c..a60f1e7 100644 --- a/src/drm_fb.c +++ b/src/drm_fb.c @@ -487,13 +487,15 @@ static void * drm_fb_page_alloc(til_fb_t *fb, void *context, til_fb_page_t *res_ p->drm_dumb_handle = map_dumb.handle; p->drm_fb_id = fb_id; - res_page->fragment.buf = map; - res_page->fragment.width = c->mode->hdisplay; - res_page->fragment.frame_width = c->mode->hdisplay; - res_page->fragment.height = c->mode->vdisplay; - res_page->fragment.frame_height = c->mode->vdisplay; - res_page->fragment.pitch = create_dumb.pitch >> 2; - res_page->fragment.stride = res_page->fragment.pitch - (c->mode->hdisplay); + *res_page = (til_fb_page_t){ + .fragment.buf = map, + .fragment.width = c->mode->hdisplay, + .fragment.frame_width = c->mode->hdisplay, + .fragment.height = c->mode->vdisplay, + .fragment.frame_height = c->mode->vdisplay, + .fragment.pitch = create_dumb.pitch >> 2, + .fragment.stride = (create_dumb.pitch >> 2) - c->mode->hdisplay, + }; return p; } diff --git a/src/modules/blinds/blinds.c b/src/modules/blinds/blinds.c index b14997e..0a8342e 100644 --- a/src/modules/blinds/blinds.c +++ b/src/modules/blinds/blinds.c @@ -66,8 +66,11 @@ static inline void draw_blind_horizontal(til_fb_fragment_t *fragment, unsigned r unsigned row_height = fragment->frame_height / count; unsigned height = roundf(t * (float)row_height); - for (unsigned y = 0; y < height; y++) - memset(fragment->buf + ((row * row_height) + y ) * fragment->pitch, 0xff, fragment->width * 4); +/* XXX FIXME: use faster block style fill/copy if til_fb gets that */ + for (unsigned y = 0; y < height; y++) { + for (unsigned x = 0; x < fragment->width; x++) + til_fb_fragment_put_pixel_unchecked(fragment, fragment->x + x, fragment->y + y + row * row_height, 0xffffffff); + } } @@ -77,8 +80,11 @@ static inline void draw_blind_vertical(til_fb_fragment_t *fragment, unsigned col unsigned column_width = fragment->frame_width / count; unsigned width = roundf(t * (float)column_width); - for (unsigned y = 0; y < fragment->height; y++) - memset(fragment->buf + y * fragment->pitch + column * column_width, 0xff, width * 4); +/* XXX FIXME: use faster block style fill/copy if til_fb gets that */ + for (unsigned y = 0; y < fragment->height; y++) { + for (unsigned x = 0; x < width; x++) + til_fb_fragment_put_pixel_unchecked(fragment, fragment->x + x + column * column_width, fragment->y + y, 0xffffffff); + } } diff --git a/src/modules/compose/compose.c b/src/modules/compose/compose.c index d672bf0..a331b3f 100644 --- a/src/modules/compose/compose.c +++ b/src/modules/compose/compose.c @@ -33,12 +33,15 @@ typedef struct compose_layer_t { typedef struct compose_context_t { unsigned n_cpus; + til_fb_fragment_t texture_fb; + compose_layer_t texture; size_t n_layers; compose_layer_t layers[]; } compose_context_t; typedef struct compose_setup_t { til_setup_t til_setup; + char *texture; size_t n_layers; char *layers[]; } compose_setup_t; @@ -93,6 +96,16 @@ static void * compose_create_context(unsigned ticks, unsigned n_cpus, til_setup_ ctxt->n_layers++; } + if (((compose_setup_t *)setup)->texture) { + til_setup_t *texture_setup = NULL; + + ctxt->texture.module = til_lookup_module(((compose_setup_t *)setup)->texture); + (void) til_module_randomize_setup(ctxt->texture.module, &texture_setup, NULL); + + (void) til_module_create_context(ctxt->texture.module, ticks, texture_setup, &ctxt->texture.module_ctxt); + til_setup_free(texture_setup); + } + return ctxt; } @@ -103,6 +116,12 @@ static void compose_destroy_context(void *context) for (int i = 0; i < ctxt->n_layers; i++) til_module_destroy_context(ctxt->layers[i].module, ctxt->layers[i].module_ctxt); + + if (ctxt->texture.module) + til_module_destroy_context(ctxt->texture.module, ctxt->texture.module_ctxt); + + free(ctxt->texture_fb.buf); + free(context); } @@ -113,8 +132,38 @@ static void compose_prepare_frame(void *context, unsigned ticks, unsigned n_cpus til_fb_fragment_clear(fragment); - for (int i = 0; i < ctxt->n_layers; i++) - til_module_render(ctxt->layers[i].module, ctxt->layers[i].module_ctxt, ticks, fragment); + if (ctxt->texture.module) { + if (!ctxt->texture_fb.buf || + ctxt->texture_fb.frame_width != fragment->frame_width || + ctxt->texture_fb.frame_height != fragment->frame_height) { + + ctxt->texture_fb = (til_fb_fragment_t){ + .buf = realloc(ctxt->texture_fb.buf, fragment->frame_height * fragment->frame_width * sizeof(uint32_t)), + + .frame_width = fragment->frame_width, + .frame_height = fragment->frame_height, + .width = fragment->frame_width, + .height = fragment->frame_height, + .pitch = fragment->frame_width, + }; + } + + ctxt->texture_fb.cleared = 0; + til_module_render(ctxt->texture.module, ctxt->texture.module_ctxt, ticks, &ctxt->texture_fb); + + til_module_render(ctxt->layers[0].module, ctxt->layers[0].module_ctxt, ticks, fragment); + + for (size_t i = 1; i < ctxt->n_layers; i++) { + til_fb_fragment_t textured = *fragment; + + textured.texture = &ctxt->texture_fb; + + til_module_render(ctxt->layers[i].module, ctxt->layers[i].module_ctxt, ticks, &textured); + } + } else { + for (size_t i = 0; i < ctxt->n_layers; i++) + til_module_render(ctxt->layers[i].module, ctxt->layers[i].module_ctxt, ticks, fragment); + } } @@ -179,6 +228,21 @@ static char * compose_random_layers_setting(void) static int compose_setup(const til_settings_t *settings, til_setting_t **res_setting, const til_setting_desc_t **res_desc, til_setup_t **res_setup) { const char *layers; + const char *texture; + const char *texture_values[] = { + "none", + "blinds", + "checkers", + "drizzle", + "julia", + "plasma", + "roto", + "stars", + "submit", + "swab", + "voronoi", + NULL + }; int r; r = til_settings_get_and_describe_value(settings, @@ -195,6 +259,20 @@ static int compose_setup(const til_settings_t *settings, til_setting_t **res_set if (r) return r; + r = til_settings_get_and_describe_value(settings, + &(til_setting_desc_t){ + .name = "Module to use for source texture, \"none\" to disable", + .key = "texture", + .preferred = texture_values[0], + .annotations = NULL, + .values = texture_values, + }, + &texture, + res_setting, + res_desc); + if (r) + return r; + /* turn layers colon-separated list into a null-terminated array of strings */ if (res_setup) { compose_setup_t *setup; @@ -257,6 +335,24 @@ static int compose_setup(const til_settings_t *settings, til_setting_t **res_set setup = new; } while (layer = strtok(NULL, ":")); + if (strcasecmp(texture, "none")) { + const til_module_t *texture_module; + + texture_module = til_lookup_module(texture); + if (!texture_module) { + til_setup_free(&setup->til_setup); + + return -EINVAL; + } + + setup->texture = strdup(texture); + if (!setup->texture) { + til_setup_free(&setup->til_setup); + + return -ENOMEM; + } + } + *res_setup = &setup->til_setup; } diff --git a/src/modules/montage/montage.c b/src/modules/montage/montage.c index 8444616..65aabe6 100644 --- a/src/modules/montage/montage.c +++ b/src/modules/montage/montage.c @@ -141,17 +141,20 @@ static int montage_fragment_tile(const til_fb_fragment_t *fragment, unsigned til xoff = x * tile_width; yoff = y * tile_height; - res_fragment->buf = fragment->buf + (yoff * fragment->pitch) + xoff; - res_fragment->x = 0; /* fragment is a new frame */ - res_fragment->y = 0; /* fragment is a new frame */ - res_fragment->width = MIN(fragment->width - xoff, tile_width); - res_fragment->height = MIN(fragment->height - yoff, tile_height); - res_fragment->frame_width = res_fragment->width; /* fragment is a new frame */ - res_fragment->frame_height = res_fragment->height; /* fragment is a new frame */ - res_fragment->stride = fragment->stride + (fragment->width - res_fragment->width); - res_fragment->pitch = fragment->pitch; - res_fragment->number = number; - res_fragment->cleared = fragment->cleared; + *res_fragment = (til_fb_fragment_t){ + .texture = fragment->texture, + .buf = fragment->buf + (yoff * fragment->pitch) + xoff, + .x = 0, /* fragment is a new frame */ + .y = 0, /* fragment is a new frame */ + .width = MIN(fragment->width - xoff, tile_width), + .height = MIN(fragment->height - yoff, tile_height), + .frame_width = MIN(fragment->width - xoff, tile_width), /* fragment is a new frame */ + .frame_height = MIN(fragment->height - yoff, tile_height), /* fragment is a new frame */ + .stride = fragment->stride + (fragment->width - MIN(fragment->width - xoff, tile_width)), + .pitch = fragment->pitch, + .number = number, + .cleared = fragment->cleared, + }; return 1; } diff --git a/src/sdl_fb.c b/src/sdl_fb.c index 249d746..b260091 100644 --- a/src/sdl_fb.c +++ b/src/sdl_fb.c @@ -202,13 +202,15 @@ static void * sdl_fb_page_alloc(til_fb_t *fb, void *context, til_fb_page_t *res_ /* rototiller wants to assume all pixels to be 32-bit aligned, so prevent unaligning pitches */ assert(!(p->surface->pitch & 0x3)); - res_page->fragment.buf = p->surface->pixels; - res_page->fragment.width = c->width; - res_page->fragment.frame_width = c->width; - res_page->fragment.height = c->height; - res_page->fragment.frame_height = c->height; - res_page->fragment.pitch = p->surface->pitch >> 2; - res_page->fragment.stride = res_page->fragment.pitch - c->width; + *res_page = (til_fb_page_t){ + .fragment.buf = p->surface->pixels, + .fragment.width = c->width, + .fragment.frame_width = c->width, + .fragment.height = c->height, + .fragment.frame_height = c->height, + .fragment.pitch = p->surface->pitch >> 2, + .fragment.stride = (p->surface->pitch >> 2) - c->width, + }; return p; } diff --git a/src/til_fb.c b/src/til_fb.c index eb14e27..f6a71a8 100644 --- a/src/til_fb.c +++ b/src/til_fb.c @@ -399,17 +399,20 @@ int til_fb_fragment_slice_single(const til_fb_fragment_t *fragment, unsigned n_f if (yoff >= fragment->height) return 0; - res_fragment->buf = fragment->buf + yoff * fragment->pitch; - res_fragment->x = fragment->x; - res_fragment->y = yoff; - res_fragment->width = fragment->width; - res_fragment->height = MIN(fragment->height - yoff, slice); - res_fragment->frame_width = fragment->frame_width; - res_fragment->frame_height = fragment->frame_height; - res_fragment->stride = fragment->stride; - res_fragment->pitch = fragment->pitch; - res_fragment->number = number; - res_fragment->cleared = fragment->cleared; + *res_fragment = (til_fb_fragment_t){ + .texture = fragment->texture, + .buf = fragment->buf + yoff * fragment->pitch, + .x = fragment->x, + .y = yoff, + .width = fragment->width, + .height = MIN(fragment->height - yoff, slice), + .frame_width = fragment->frame_width, + .frame_height = fragment->frame_height, + .stride = fragment->stride, + .pitch = fragment->pitch, + .number = number, + .cleared = fragment->cleared, + }; return 1; } @@ -435,17 +438,20 @@ int til_fb_fragment_tile_single(const til_fb_fragment_t *fragment, unsigned tile xoff = x * tile_size; yoff = y * tile_size; - res_fragment->buf = fragment->buf + (yoff * fragment->pitch) + (xoff); - res_fragment->x = fragment->x + xoff; - res_fragment->y = fragment->y + yoff; - res_fragment->width = MIN(fragment->width - xoff, tile_size); - res_fragment->height = MIN(fragment->height - yoff, tile_size); - res_fragment->frame_width = fragment->frame_width; - res_fragment->frame_height = fragment->frame_height; - res_fragment->stride = fragment->stride + (fragment->width - res_fragment->width); - res_fragment->pitch = fragment->pitch; - res_fragment->number = number; - res_fragment->cleared = fragment->cleared; + *res_fragment = (til_fb_fragment_t){ + .texture = fragment->texture, + .buf = fragment->buf + (yoff * fragment->pitch) + (xoff), + .x = fragment->x + xoff, + .y = fragment->y + yoff, + .width = MIN(fragment->width - xoff, tile_size), + .height = MIN(fragment->height - yoff, tile_size), + .frame_width = fragment->frame_width, + .frame_height = fragment->frame_height, + .stride = fragment->stride + (fragment->width - MIN(fragment->width - xoff, tile_size)), + .pitch = fragment->pitch, + .number = number, + .cleared = fragment->cleared, + }; return 1; } diff --git a/src/til_fb.h b/src/til_fb.h index 55e9b6c..3ebede7 100644 --- a/src/til_fb.h +++ b/src/til_fb.h @@ -1,26 +1,31 @@ #ifndef _TIL_FB_H #define _TIL_FB_H +#include #include #include #include "til_settings.h" #include "til_setup.h" +#include "til_util.h" + +typedef struct til_fb_fragment_t til_fb_fragment_t; /* All renderers should target fb_fragment_t, which may or may not represent * a full-screen mmap. Helpers are provided for subdividing fragments for * concurrent renderers. */ typedef struct til_fb_fragment_t { - uint32_t *buf; /* pointer to the first pixel in the fragment */ - unsigned x, y; /* absolute coordinates of the upper left corner of this fragment */ - unsigned width, height; /* width and height of this fragment */ - unsigned frame_width; /* width of the frame this fragment is part of */ - unsigned frame_height; /* height of the frame this fragment is part of */ - unsigned stride; /* number of 32-bit words from the end of one row to the start of the next */ - unsigned pitch; /* number of 32-bit words separating y from y + 1, including any padding */ - unsigned number; /* this fragment's number as produced by fragmenting */ - unsigned cleared:1; /* if this fragment has been cleared since last flip */ + til_fb_fragment_t *texture; /* optional source texture when drawing to this fragment */ + uint32_t *buf; /* pointer to the first pixel in the fragment */ + unsigned x, y; /* absolute coordinates of the upper left corner of this fragment */ + unsigned width, height; /* width and height of this fragment */ + unsigned frame_width; /* width of the frame this fragment is part of */ + unsigned frame_height; /* height of the frame this fragment is part of */ + unsigned stride; /* number of 32-bit words from the end of one row to the start of the next */ + unsigned pitch; /* number of 32-bit words separating y from y + 1, including any padding */ + unsigned number; /* this fragment's number as produced by fragmenting */ + unsigned cleared:1; /* if this fragment has been cleared since last flip */ } til_fb_fragment_t; /* This is a page handle object for page flip submission/life-cycle. @@ -70,12 +75,20 @@ static inline int til_fb_fragment_contains(til_fb_fragment_t *fragment, int x, i } +/* gets a pixel from the fragment, no bounds checking is performed. */ +static inline uint32_t til_fb_fragment_get_pixel_unchecked(til_fb_fragment_t *fragment, int x, int y) +{ + return fragment->buf[(y - fragment->y) * fragment->pitch + x - fragment->x]; +} + + /* puts a pixel into the fragment, no bounds checking is performed. */ static inline void til_fb_fragment_put_pixel_unchecked(til_fb_fragment_t *fragment, int x, int y, uint32_t pixel) { - uint32_t *pixels = fragment->buf + (y - fragment->y) * fragment->pitch; + if (fragment->texture) + pixel = til_fb_fragment_get_pixel_unchecked(fragment->texture, x, y); - pixels[x - fragment->x] = pixel; + fragment->buf[(y - fragment->y) * fragment->pitch + x - fragment->x] = pixel; } @@ -91,8 +104,31 @@ static inline int til_fb_fragment_put_pixel_checked(til_fb_fragment_t *fragment, } -/* fill a fragment with an arbitrary pixel */ -static inline void til_fb_fragment_fill(til_fb_fragment_t *fragment, uint32_t pixel) +/* copy a fragment, x,y,width,height are absolute coordinates within the frames, and will be clipped to the overlapping fragment areas */ +static inline void til_fb_fragment_copy(til_fb_fragment_t *dest, int x, int y, int width, int height, til_fb_fragment_t *src) +{ + int X = MAX(dest->x, src->x); + int Y = MAX(dest->y, src->y); + int W = MIN(dest->x + dest->width, src->x + src->width) - X; + int H = MIN(dest->y + dest->height, src->y + src->height) - Y; + + assert(W >= 0 && H >= 0); + + /* XXX FIXME TODO */ + /* XXX FIXME TODO */ + /* XXX FIXME TODO */ + /* this is PoC fast and nasty code, optimize this to at least bulk copy rows of pixels */ + /* XXX FIXME TODO */ + /* XXX FIXME TODO */ + /* XXX FIXME TODO */ + for (int v = 0; v < H; v++) { + for (int u = 0; u < W; u++) + til_fb_fragment_put_pixel_unchecked(dest, X + u, Y + v, til_fb_fragment_get_pixel_unchecked(src, X + u, Y + v)); + } +} + + +static inline void _til_fb_fragment_fill(til_fb_fragment_t *fragment, uint32_t pixel) { uint32_t *buf = fragment->buf; @@ -105,13 +141,24 @@ static inline void til_fb_fragment_fill(til_fb_fragment_t *fragment, uint32_t pi } +/* fill a fragment with an arbitrary pixel */ +static inline void til_fb_fragment_fill(til_fb_fragment_t *fragment, uint32_t pixel) +{ + if (!fragment->texture) + return _til_fb_fragment_fill(fragment, pixel); + + /* when a texture is present, pixel is ignored and instead sourced from fragment->texture->buf[y*pitch+x] */ + til_fb_fragment_copy(fragment, fragment->x, fragment->y, fragment->width, fragment->height, fragment->texture); +} + + /* clear a fragment */ static inline void til_fb_fragment_clear(til_fb_fragment_t *fragment) { if (fragment->cleared) return; - til_fb_fragment_fill(fragment, 0); + _til_fb_fragment_fill(fragment, 0); fragment->cleared = 1; } -- cgit v1.2.3