Age | Commit message (Collapse) | Author |
|
modules/checkers w/fill_module=$module requires a consistent
mapping of cpu to fragnum since it creates a per-cpu
til_module_context_t for the fill_module.
The existing implementation for threaded rendering maximizes
performance by letting *any* scheduled to run thread advance
fragnum atomically and render the acquired fragnum
indiscriminately. A side effect of this is any given frame, even
rendered by the same module, will have a random mapping of
cpus/threads to fragnums.
With this change, the simple til_module_t.prepare_frame() API of
returning a bare fragmenter function is changed to instead return
a "frame plan" in til_frame_plan_t. Right now til_frame_plan_t
just contains the same fragmenter as before, but also has a
.cpu_affinity member for setting if the frame requires a stable
relationship of cpu/thread to fragnum.
Setting .cpu_affinity should be avoided if unnecessary, and that
is the default if you don't mention .cpu_affinity at all when
initializing the plan in the ergonomic manner w/designated
initializers. This is because the way .cpu_affinity is
implemented will leave threads spinning while they poll for
*their* next fragnum using atomic intrinsics. There's probably
some room for improvement here, but this is good enough for now
to get things working and correct.
|
|
The palette mutates across frames, on a context-specific
schedule. Meaning the palette is per-context, so move it into
roto_context_t.
The phase also needs to be driven by ticks. And when ticks
doesn't change in cases where the same context is rendered
manifold in the same frame, the phase shouldn't move.
|
|
This way if a given context gets rendered repeatedly for the same
tick, no movement occurs, until ticks changes.
|
|
Also wire this up to the til_module_context_new() helper and
all its callers.
This is in preparation for modules doing more correct delta-T
derived animation.
|
|
While testing an experimental checkers w/fill_module=blinds with
ASAN it became clear this module is making flawed assumptions
about fragment->frame_{width,height} and fragment->{width,height}
being equal.
When used by checkers for filling cells, there are situations
where the edge cell fragments need to describe a frame slightly
larger than the drawn area, because the cell size doesn't align
perfectly to the overall window/screen dimensions. So in these
cases the synthesized frame will still be a full cell's
dimensions while the width,height serve to clip within that area.
If modules aren't properly clipping their rendering, instead just
using frame_{width,height}, then they will have to use the
_checked() variants to ensure clipping occurs properly on a
per-pixel (slower) basis.
|
|
Contexts aren't void* anymore, and free the contexts array too on
failure.
|
|
This initializer could perform an out-of-bounds read since it
occurs before the n_modules bounds check.
Since the variable isn't even being used anymore just get rid of
this. Also found via ASAN.
|
|
While testing a checkers change that fills cells using other
modules, ASAN kept tripping on pixbounce:
==147817==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7fc78a31c10c at pc 0x55b30cd406e2 bp 0x7fc790afd0d0 sp 0x7fc790afd0c8
WRITE of size 4 at 0x7fc78a31c10c thread T2
#0 0x55b30cd406e1 in til_fb_fragment_put_pixel_unchecked pixbounce.c
#1 0x55b30cd3f8ae in pixbounce_render_fragment pixbounce.c
#2 0x55b30cd1dffb in module_render_fragment til.c
#3 0x55b30cd1d989 in til_module_render (/home/foo/src/rototiller/build/src/rototiller+0x134989)
#4 0x55b30cd22534 in checkers_render_fragment checkers.c
#5 0x55b30cd14681 in thread_func til_threads.c
#6 0x7fc792b3d5c1 in start_thread pthread_create.c
#7 0x7fc792bc2583 in __clone (/usr/lib/libc.so.6+0x112583)
0x7fc78a31c10c is located 2276 bytes to the right of 1228840-byte region [0x7fc78a1ef800,0x7fc78a31b828)
allocated by thread T0 here:
#0 0x55b30cccf219 in __interceptor_malloc (/home/foo/src/rototiller/build/src/rototiller+0xe6219)
#1 0x7fc792d0e528 (/usr/lib/libSDL2-2.0.so.0+0x39528)
Thread T2 created by T0 here:
#0 0x55b30cc3cfa8 in pthread_create (/home/foo/src/rototiller/build/src/rototiller+0x53fa8)
#1 0x55b30cd13fff in til_threads_create (/home/foo/src/rototiller/build/src/rototiller+0x12afff)
#2 0x55b30cd1d573 in til_init (/home/foo/src/rototiller/build/src/rototiller+0x134573)
#3 0x55b30cd08f6c in main (/home/foo/src/rototiller/build/src/rototiller+0x11ff6c)
#4 0x7fc792add30f in __libc_start_call_main libc-start.c
SUMMARY: AddressSanitizer: heap-buffer-overflow pixbounce.c in til_fb_fragment_put_pixel_unchecked
Shadow bytes around the buggy address:
0x0ff97145b7d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b7e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b7f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0ff97145b820: fa[fa]fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b840: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b850: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b860: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0ff97145b870: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==147817==ABORTING
---
Rather than spend time digging into pixbounce's arithmetic, just
using the checked variant for now.
|
|
This introduces a very naive unoptimized moire interference
pattern module, it's rather slow complete with a sqrtf() per
pixel per center.
|
|
- modules now allocate their contexts using
til_module_context_new() instead of [cm]alloc().
- modules simply embed til_module_context_t at the start of their
respective private context structs, if they do anything with
contexts
- modules that do nothing with contexts (lack a create_context()
method), will now *always* get a til_module_context_t supplied
to their other methods regardless of their create_context()
presence. So even if you don't have a create_context(), your
prepare_frame() and/or render_fragment() methods can still
access seed and n_cpus from within the til_module_context_t
passed in as context, *always*.
- modules that *do* have a create_context() method, implying they
have their own private context type, will have to cast the
til_module_context_t supplied to the other methods to their
private context type. By embedding the til_module_context_t at
the *start* of their private context struct, a simple cast is
all that's needed. If it's placed somewhere else, more
annoying container_of() style macros are needed - this is
strongly discouraged, just put it at the start of struct.
- til_module_create_context() now takes n_cpus, which may be set
to 0 for automatically assigning the number of threads in its
place. Any non-zero value is treated as an explicit n_cpus,
primarily intended for setting it to 1 for single-threaded
contexts necessary when embedded within an already-threaded
composite module.
- modules like montage which open-coded a single-threaded render
are now using the same til_module_render_fragment() as
everything else, since til_module_create_context() is accepting
n_cpus.
- til_module_create_context() now produces a real type, not void
*, that is til_module_context_t *. All the other module
context functions now operate on this type, and since
til_module_context_t.module tracks the module this context
relates to, those functions no longer require both the module
and context be passed in. This is especially helpful for
compositing modules which do a lot of module context creation
and destruction; the module handle is now only needed to create
the contexts. Everything else operating on that context only
needs the single context pointer, not module+context pairs,
which was unnecessarily annoying.
- if your module's context can be destroyed with a simple free(),
without any deeper knowledge or freeing of nested pointers, you
can now simply omit destroy_context() altogether. When
destroy_context() is missing, til_module_context_free() will
automatically use libc's free() on the pointer returned from
your create_context() (or on the pointer that was automatically
created if you omitted create_context() too, for the
bare til_module_context_t that got created on your behalf
anyways).
For the most part, these changes don't affect module creation.
In some ways this eases module creation by making it more
convenient access seed and n_cpus if you had no further
requirement for a context struct.
In other ways it's slightly annoying to have to do type-casts
when you're working with your own context type, since before it
was all void* and didn't require casts when assigning to your
typed context variables.
The elimination for requiring a destroy_context() method in
simple free() of private context scenarios removes some
boilerplate in simple cases.
I think it's a wash for module writers, or maybe a slight win for
the simple cases.
|
|
I don't think rototiller is an appropriate place for being so
uncooperative, if someone gets the case wrong anywhere just make
it work. We should avoid making different things so subtly
different that case alone is the distinction anyways, so I don't
see this creating any future namespace collision problems.
|
|
A bunch of these have just been done
|
|
I didn't like the too fast spins where you can't even really get
a read on what's going on in the shape.
Suspect this will get tweaked more in the future...
|
|
This is kind of experimental, not sure how I feel about it.
pinch=0..1 with a bunch of fractions, 0 disables it.
pinch_spin=-1..1 same as spin=
pinches=1..10 number of pinches, which come in pairs
This applies to all the current shapes, for a tour:
--module=rtv,channels=shapes,duration=2,context_duration=2,snow_module=none --defaults
I think the speeds might go to high atm, I kind of liked the
slower spins before all this more.
|
|
woops
|
|
scale=1-.5 with a few other fractions in between
Mostly added for the sake of rtv/compose where modules currently
always go full-frame, which can be really annoying sometimes for
shapes without any variety in the scale.
When checkers starts filling cells using modules like shapes,
it'll be interesting to see how this fares there since it'll
probably be randomizing the settings too. At least floored at
50% should still produce something legible in /most/ of the
checkers cell sizes. Definitely won't look like anything in the
smaller end of the checkers sizes though... hrm.
|
|
Introduces spin={-1,0,1} with a few intermediate fractions on
both sides of 0. Controls rate and direction.
As-is these multiple choice style options don't let you
explicitly set a value in rototiller on the commandline that
isn't in the set.
There should probably be a flag in the desc we can set when
bypassing the available options is tolerable, probably when
regex's start getting enforced.
That way rototiller's commandline setting parsing can just lean
on the regex, and if the desc says anything can come in that
passes the regex even if it's not in the values set, this can all
still work and have something resembling input validation.
At the end of the day, the multiple choice value sets are
supposed to be a convenience/guide to a sane variety of values
and of particular utility to randomizers like used by
rtv/montage/compose, but also GUI setting selectors like in
glimmer.
They're not intended to get in the way preventing development
from accessing explicit values of arbitrary precision which can
be really necessary especially when trying to determine what's
best for going in the values set.
|
|
Replace the hard-coded 5 points with a points=3-20 setting
Nothing fancy going on here
|
|
|
|
- clear padding when {letter,pillar}boxed
- limit costly rendering to shape size area when boxed
- fix <= inclusion tests in circle and rhombus: s/<=/</
|
|
I'm not sure why this was being done, it was probably just
vestigial from whatever I bootstrapped the compose module with
and never thought to remove it.
The first compose layer rendered will clear the frame if
necessary. By compose doing it ahead of time, it's performing
potentially unnecessary work clearing if the first layer is a
frame-filler style render where no clear is ever performed.
|
|
This is a mostly mechanical change of using rand_r() in place of
rand(), using the provided seed as the seed state.
There's some outstanding rand()s outside of create_context()
which should probably get switched over, with the seed being
stowed in the context struct. I didn't bother going deeper on
this at the moment in the interests of getting to sleep soon.
|
|
n_cells is a size_t, use %zu
clang complained, gcc doesn't, huh
|
|
In the recent surge of ADD-style rtv+compose focused development,
a bunch of modules were changed to randomize initial states at
context_create() so they wouldn't be so repetitive.
But the way this was done in a way that made it impossible to
suppress the randomized initial state, which sometimes may be
desirable in compositions. Imagine for instance something like
the checkers module, rendering one module in the odd cells, and
another module into the even cells. Imagine if these modules are
actually the same, but if checkers used one seed for all the odd
cells and another seed for all the even cells. If the modules
used actually utilized the seed provided, checkers would be able
to differentiate the odd from even by seeding them differently
even when the modules are the same.
This commit is a step in that direction, but rototiller and all
the composite modules (rtv,compose,montage) are simply passing
rand() as the seeds. Also none of the modules have yet been
modified to actually make use of these seeds.
Subsequent commits will update modules to seed their
pseudo-randomized initial state from the seed value rather than
always calling things like rand() themselves.
|
|
Mostly for compositing purposes, here will be a corpus of 2D
shapes, parameterized/procedurally generated and able to rotate
and perhaps have other dynamics added.
What shapes are there presently I had started implementing in
checkers as "styles", before realizing they really should just be
a separate module checkers can call into.
Not terribly interesting by itself, but as blinds and checkers
demonstrated, these things deliver a lot of value in
compositional situations. They're creating the palette to draw
from.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We don't actually want to produce indices 0-width and 0-height
|
|
|
|
There's more to cleanup in rtv destruction, but this is the major
one.
|
|
Found via -fsanitize=address, this is a quick and dirty way to
prevent the OOB access without adding more conditionals, just
prevent scaling the fragment dimensions to the full grid
dimensions.
This could be done better by reworking things a bit and putting
zero at the center of the grid with a -1..+1 mapping, so rounding
towards zero would land in the middle as opposed to off the
start, with the existing .5f offset.
But for now just fix the bug, nobody will notice the slight LCD
overscan-style difference of bilerp=on vs. off due to this way.
|
|
This was causing the snow module to scribble via montage, since
snow uses per-cpu rand seeds indexed using the cpu value.
I didn't dig in to see if this was a vestigial thing where
fragnum once was passed as a parameter (it's also in
til_fb_fragment_t.number, but probably wasn't always that way).
But it's now used as a cpu idx, but since they're the same type
nothing complained, they say programming is hard.
|
|
Also shortened the durations across the board, and defaulted snow
to "none".
With "compose" being another meta module, and the randomized
settings, this actually gives a more interesting grand tour of
everything possible.
|
|
Just adds TIL_FB_DRAW_FLAG_TEXTURABLE so callers can granularly
inhibit texturing if desired.
|
|
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.
|
|
Same as for roto and plasma...
|
|
As with roto, it's important to differentiate plasma instances
when layered... plus it's kind of boring to always start plasma
identically.
|
|
Just something quick and dirty to differentiate roto layers, plus
it's kind of boring having roto always start at the same state.
|
|
It's no longer necessary to specify both snow_duration=0 AND
snow_module=none to disable snow, just specify snow_module=none.
A future commit should really make describing the "snow_duration"
setting contingent on snow_module != "none".
|
|
Just one case, modules/submit, was using 32x32 tiles and is now
using 64x64. I don't expect it to make any difference.
While here I fixed up the num_cpus/n_cpus naming inconsistencies,
normalizing on n_cpus.
|
|
Fragmenting is often dimensioned according to the number of cpus,
and by not supplying this to the fragmenter it was made rather
common for module contexts to plumb this themselves - in some
cases incorporating a context type/create/destroy rigamarole
for the n_cpus circuit alone.
So just plumb it in libtil, and the prepare_frame functions can
choose to ignore it if they have something more desirable onhand.
Future commits will remove a bunch of n_cpus from module contexts
in favor of this.
|
|
The code was deliberately showing a single frame of snow when
snow_duration=0, since the durations are integral seconds ther
was no way to have a sub 1-second snow otherwise.
snow_module=none just means cleared for the snow_duration, not no
snow mechanism whatsoever. So the combination of 0 duration and
"none" was still flashing a single blank frame.
With this commit the combination of "none" and 0 snow_duration
prevents even a single frame of blank snow from being rendered.
|
|
cosmetic indentation fixup
|
|
Mechanical rename to something less ambiguous in a world with
til_settings_t, til_setting_t, and til_setup_t etc.
|
|
This adds a voronoi diagram module, which when used as an overlay
produces a mosaic effect.
Some settings:
cells=N number of voronoi cells
randomize={on,off} randomizes the cell locations every frame
dirty={on,off} uses a faster sloppy/dithery-looking method
Some TODO items:
- use a more space efficient representation of the distance
buffer, maybe use uint16_t relative offsets into the cells
rather than pointers - capping their quantity to 64KiB
- anti-alias edges between cells
|
|
compute scaled x/y coordinates less often by reusing them
|