From e9e86a017d0a0e5857b9f2c9ecf29b3bdc0ae26a Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Sun, 24 Jul 2022 00:23:51 -0700 Subject: modules/checkers: center unaligned checkers scenarios This introduces a bespoke fragmenter for checkers. The generic til_fb tiler isn't concerned with aesthetics so it doesn't particularly care if clipped tiles are asymmetrically distributed. It worked fine to get checkers developed and working, but it's really unattractive to have the whole be off-centered when the checkers don't perfectly align with the frame size. There's some gross aspects like leaving the frame_{width,height} to be corrected at render time so render_fragment can access the incoming frame_width for cell state determination. --- src/modules/checkers/checkers.c | 78 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/modules/checkers/checkers.c b/src/modules/checkers/checkers.c index 5554e21..20ecd63 100644 --- a/src/modules/checkers/checkers.c +++ b/src/modules/checkers/checkers.c @@ -130,11 +130,73 @@ static void checkers_destroy_context(til_module_context_t *context) } +/* This is derived from til_fb_fragment_tile_single() with two variations: + * 1. when the size doesn't align with frame size, the start tiles are offset + * to center the checkers letting the edge checkers all clip as needed + * 2. the incoming frame width isn't propagated down to the tiled fragments, + * though for the potentially clipped boundary tiles the frame_{width,height} + * won't match the incoming width,height. + * + * XXX note this fragmenter in particular really exercises fill_modules' correct handling + * of frame vs. fragment dimensions and clipping semantics + */ +int checkers_fragment_tile_single(const til_fb_fragment_t *fragment, unsigned tile_size, unsigned number, til_fb_fragment_t *res_fragment) +{ + unsigned w = fragment->width / tile_size, h = fragment->height / tile_size; + unsigned tiled_w = w * tile_size, tiled_h = h * tile_size; + unsigned x, y, xoff, yoff, xshift = 0, yshift = 0; + + /* Detect the need for fractional tiles on both axis and shift the fragments + * to keep the overall checkered output centered. This complicates res_fragment.{x,y,width,height} + * calculations for the peripheral checker tiles as those must clip when shifted. + */ + if (tiled_w < fragment->width) { + tiled_w += tile_size; + xshift = (tiled_w - fragment->width) >> 1; + w++; + } + + if (tiled_h < fragment->height) { + tiled_h += tile_size; + yshift = (tiled_h - fragment->height) >> 1; + h++; + } + + y = number / w; + if (y >= h) + return 0; + + x = number - (y * w); + + xoff = x * tile_size; + yoff = y * tile_size; + + *res_fragment = (til_fb_fragment_t){ + .texture = fragment->texture, + .buf = fragment->buf + (yoff * fragment->pitch) - (y ? (yshift * fragment->pitch) : 0) + (xoff - (x ? xshift : 0)), + .width = MIN(fragment->width - (xoff - xshift), x ? tile_size : (tile_size - xshift)), + .height = MIN(fragment->height - (yoff - yshift), y ? tile_size : (tile_size - yshift)), + .x = x ? 0 : xshift, + .y = y ? 0 : yshift, + // this is a little janky but leave frame_width to be set by render_fragment + // so it can use the old frame_width for determining checkered state + .frame_width = fragment->width, // becomes tile_size + .frame_height = fragment->height, // becomes tile_size + .stride = fragment->stride + (fragment->width - MIN(fragment->width - (xoff - xshift), x ? tile_size : (tile_size - xshift))), + .pitch = fragment->pitch, + .number = number, + .cleared = fragment->cleared, + }; + + return 1; +} + + static int checkers_fragmenter(til_module_context_t *context, const til_fb_fragment_t *fragment, unsigned number, til_fb_fragment_t *res_fragment) { checkers_context_t *ctxt = (checkers_context_t *)context; - return til_fb_fragment_tile_single(fragment, ctxt->setup.size, number, res_fragment); + return checkers_fragment_tile_single(fragment, ctxt->setup.size, number, res_fragment); } @@ -174,13 +236,15 @@ static void checkers_render_fragment(til_module_context_t *context, unsigned tic switch (ctxt->setup.pattern) { case CHECKERS_PATTERN_CHECKERED: { - unsigned tiles_per_row; + unsigned tiles_per_row, row, col; tiles_per_row = fragment->frame_width / ctxt->setup.size; if (tiles_per_row * ctxt->setup.size < fragment->frame_width) tiles_per_row++; - state = (fragment->number + (fragment->y / ctxt->setup.size) * !(tiles_per_row & 0x1)) & 0x1; + row = fragment->number / tiles_per_row; + col = fragment->number % tiles_per_row; + state = (row ^ col) & 0x1; break; } case CHECKERS_PATTERN_RANDOM: @@ -188,6 +252,10 @@ static void checkers_render_fragment(til_module_context_t *context, unsigned tic break; } + /* now that state has been determined, set the frame size */ + fragment->frame_width = ctxt->setup.size; + fragment->frame_height = ctxt->setup.size; + switch (ctxt->setup.dynamics) { case CHECKERS_DYNAMICS_ODD: break; @@ -224,10 +292,6 @@ static void checkers_render_fragment(til_module_context_t *context, unsigned tic if (!ctxt->setup.fill_module) til_fb_fragment_fill(fragment, flags, color); else { - fragment->frame_width = ctxt->setup.size; - fragment->frame_height = ctxt->setup.size; - fragment->x = fragment->y = 0; - /* TODO: we need a way to send down color and flags, and use the module render as a brush of sorts */ til_module_render(ctxt->fill_module_contexts[cpu], ticks, fragment); } -- cgit v1.2.3