Age | Commit message (Collapse) | Author |
|
There needs to be a way to address module context instances
by name externally, in a manner complementary to settings and
taps.
This commit adds a string-based path to til_module_context_t, and
modifies til_module_create_context() to accept a parent path
which is then concatenated with the name of the module to produce
the module instance's new path.
The name separator used in the paths is '/' just like filesystem
paths, but these paths have no relationship to filesystems or
files.
The root module context creation in rototiller's main simply
passes "" as the parent path, resulting in a "/" root as one
would expect.
There are some obvious complications introduced here however:
- checkers in particular creates a context per cpu, simply using
the same seed and setup to try make the contexts identical at
the same ticks value. With this commit I'm simply passing the
incoming path as the parent for creating those contexts, but
it's unclear to me if that will work OK. With an eye towards
taps deriving their parent path from the context path, I guess
these taps would all get the same parent and hash to the same
value despite being duplicated. Maybe it Just Works, but one
thing is clear - there won't be any way to address the per-cpu
taps as-is. Maybe that's desirable though, there's probably
not much use in trying to control the taps at the CPU
granularity.
- when the recursive settings stuff lands, it should bring along
the ability to explicitly name settings blocks. Those names
should override the module name in constructing the path.
I've noted as such in the code.
- these paths probably need to be hashed @ initialization time
so there needs to be a hash function added to til, and a hash
value accompanying the name in the module context. It'd be
dumb to keep recomputing the hash when these paths get used
for hash table lookups multiple times per frame...
there's probably more I'm forgetting right now, but this seems
like a good first step.
fixup root path
|
|
Preparatory commit for enabling cloneable/swappable fragments
There's an outstanding issue with the til_fb_page_t submission,
see comments. Doesn't matter for now since cloning doesn't happen
yet, but will need to be addressed before they do.
|
|
These modules have been doing their work in prepare_frame(), but
aren't actually threaded modules and don't return a frame plan
from prepare_frame() nor do they provide a render_fragment() to
complement the prepare_frame().
The convention thus far has been that single-threaded modules
just provide a render_fragment and by not providing a
prepare_frame they will be executed serially.
These two modules break the contract in a sense by using
prepare_frame() without following up with render_fragment().
I'm not sure why it happened that way, maybe at one time
prepare_frame() had access to some things that render_fragment()
didn't.
In any case, just make these use render_fragment() like any other
simple non-threaded module would.
This was actually causing a crash when n_cpus=1 because
module_render_fragment() was assuming the prepare_frame() branch
would include a render_fragment(). It should probably be
asserting as such.
|
|
just fixing up some vestigial rand() invocations to use the seed
|
|
til_setting_desc_t.random() and til_module_randomize_setup() now
take seeds.
Note they are not taking a pointer to a shared seed, but instead
receive the seed by value.
If a caller wishes the seed to evolve on every invocation into
these functions, it should simply insert a rand_r(&seed) in
producing the supplied seed value.
Within a given randomizer, the seed evolves when appropriate.
But isolating the effects by default seems appropriate, so
callers can easily have determinism within their respective scope
regardless of how much nested random use occurs.
|
|
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.
|
|
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.
|
|
- 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.
|
|
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.
|
|
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.
|
|
|
|
There's more to cleanup in rtv destruction, but this is the major
one.
|
|
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.
|
|
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.
|
|
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 should plug a bulk of the setup leaks. Some of the
free_funcs still need to be changed to bespoke ones in modules
that allocate nested things in their respective setup, so those
are still leaking the nested things which are usually just a
small strdup of some kind.
|
|
This brings something resembling an actual type to the private
objects returrned in *res_setup. Internally libtil/rototiller
wants this to be a til_setup_t, and it's up to the private users
of what's returned in *res_setup to embed this appropriately and
either use container_of() or casting when simply embedded at the
start to go between til_setup_t and their private containing
struct.
Everywhere *res_setup was previously allocated using calloc() is
now using til_setup_new() with a free_func, which til_setup_new()
will initialize appropriately. There's still some remaining work
to do with the supplied free_func in some modules, where free()
isn't quite appropriate.
Setup freeing isn't actually being performed yet, but this sets
the foundation for that to happen in a subsequent commit that
cleans up the setup leaks.
Many modules use a static default setup for when no setup has
been provided. In those cases, the free_func would be NULL,
which til_setup_new() refuses to do. When setup freeing actually
starts happening, it'll simply skip freeing when
til_setup_t.free_func is NULL.
|
|
This makes the arg return optional by using a res_arg pointer,
instead returning -ENOMEM when it would have returned NULL on
allocation failures.
This also makes it possible to detect when no setup was
performed, by returning 0 in such a case. Now returns 1 when
setup occurs and res pointers populated.
|
|
This commit pulls the setup randomizer out of rtv into libtil
proper, so other modules may make use of it.
Other than adding an assert no functional changes occurred.
It may make sense to split this into two functions; one which
takes a til_module_t as-is, and a lower-level bare setup function
callback based function that doesn't know about til_module_t the
former would call into. That way generic setup randomization can
occur (the same setup machinery is used in video contexts for
example) without necessarily having a til_module_t on hand.
|
|
Mechanical renaming of "zero" to "clear" throughout for this
context.
|
|
Now modules allocate and return an opaque setup pointer in
res_setup when they implement a setup method.
Defaults are utilized when ${module}_create_context() receives a
NULL setup. The default setup used in this case should match the
defaults/preferred values emitted by the module's setup method.
But performing setup should always be optional, so a NULL setup
provided to create_context() is to be expected.
No cleanup of these setup instances is currently performed, so
it's a small memory leak for now. Since these are opaque and may
contain nested references to other allocations, simply using
free() somewhere in the frontend is insufficient. There will
probably need to be something like a til_module_t.setup_free()
method added in the future which modules may assign libc's free()
to when appropriate, or their own more elaborate version.
Lifecycle for the settings is very simple; the setup method
returns an instance, the caller is expected to free it when no
longer needed (once free is implemented). The create_context
consumer of a given setup must make its own copy of the settings
if necessary, and may not keep a reference - it must assume the
setup will be freed immediately after create_context() returns.
This enables the ability to reuse a setup instance across
multiple create_context() calls if desired, one can imagine
something like running the same module with the same settings
multiple times across multiple displays for instance. If the
module has significant entropy the output will differ despite
being configured identically...
With this commit one may change settings for any of the modules
*while* the modules are actively rendering a given context, and
the settings should *not* be visible. They should only affect
the context they're supplied to.
|
|
This is a preparatory commit for cleaning up the existing sloppy
global-ish application of settings during the iterative _setup()
call sequences.
Due to how this has evolved from a very rudimentary thing
enjoying many assumptions about there ever only being a single
module instance being configured by the settings, there's a lot
of weirdness and inconsistency surrounding module setup WRT
changes being applied instantaneously to /all/ existing and
future context's renderings of a given module vs. requiring a new
context be created to realize changes.
This commit doesn't actually change any of that, but puts the
plumbing in place for the setup methods to allocate and
initialize a private struct encapsulating the parsed and
validated setup once the settings are complete. This opaque
setup pointer will then be provided to the associated
create_context() method as the setup pointer. Then the created
context can configure itself using the provided setup when
non-NULL, or simply use defaults when NULL.
A future commit will update the setup methods to allocate and
populate their respective setup structs, adding the structs as
needed, as well as updating their create_context() methods to
utilize those setups.
One consequence of these changes when fully realized will be that
every setting change will require a new context be created from
the changed settings for the change to be realized.
For settings appropriately manipulated at runtime the concept of
knobs was introduced but never finished. That will have to be
finished in the future to enable more immediate/interactive
changing of settings-like values appropriate for interactive
manipulation
|
|
Just mechanical replacement of some remaining ad-hoc
til_module_t.create_context() calls.
The montage module continues using an ad-hoc call because it
forces num_cpus=1 since it's already a threaded using a fragment
per module's tile. This suggests the til_module_create_context()
call should probably accept a num_cpus parameter, perhaps
treating a 0 value as the "automagic" discover value so callers
can explicitly set it when necessary.
|
|
Now that til_setting_t.desc is not only a thing, but a thing that
is intended to be refreshed regularly in the course of things
like GUI interactive settings construction, it's not really
appropriate to try even act like this these are const anymore.
|
|
Mechanically replaced ad-hoc til_module_t.destroy_context()
invocations with helper calls.
|
|
Always only capitalize the first letter, never capitalize like
titles.
|
|
Originally the thinking was that rototiller modules would become
dlopen()ed shared objects, and that it would make sense to let
them be licensed differently.
At this time only some modules I have written were gplv3, Phil's
modules are all gplv2, and I'm not inclined to pivot towards a
dlopen model.
So this commit drops the license field from til_module_t,
relicenses my v3 code to v2, and adds a gplv2 LICENSE file to the
source root dir. As of now rototiller+libtil and all its modules
are simply gplv2, and anything linking in libtil must use a gplv2
compatible license - the expectation is that you just use gplv2.
|
|
The existing iterative *_setup() interface only described
settings not found, quietly accepting usable settings already
present in the til_settings_t.
This worked fine for the existing interactive text setup thing,
but it's especially problematic for providing a GUI setup
frontend.
This commit makes it so the *_setup() methods always describe
undescribed settings they recognize, leaving the setup frontend
loop calling into the *_setup() methods to both apply the
description validation if wanted and actually tie the description
to respective setting returned by the _setup() methods as being
related to the returned description.
A new helper called til_settings_get_and_describe_value() has
been introduced primarily for use of module setup methods to
simplify this nonsense, replacing the til_settings_get_value()
calls and surrounding logic, but retaining the til_setting_desc_t
definitions largely verbatim.
This also results in discarding of some ad-hoc
til_setting_desc_check() calls, now that there's a centralized
place where settings become "described" (setup_interactively in
the case of rototiller).
Now a GUI frontend (like glimmer) would just provide its own
setup_interactively() equivalent for constructing its widgets for
a given *_setup() method's chain of returned descs. Whereas in
the past this wasn't really feasible unless there was never going
to be pre-supplied settings.
I suspect the til_setting_desc_check() integration into
setup_interactively() needs more work, but I think this is good
enough for now and I'm out of spare time for the moment.
|
|
Largely mechanical rename of librototiller -> libtil, but
introducing a til_ prefix to all librototiller (now libtil)
functions and types where a rototiller prefix was absent.
This is just a step towards a more libized librototiller, and til
is just a nicer to type/read prefix than rototiller_.
|
|
This manifests in the current unconfigured rtv glimmer shows, since
the default is a the "none" module when no settings are applied.
But it turns out this isn't just a glimmer problem, "none" is advertised
in the settings as a blanking alternative to snow. So it's actually
broken in rototiller as well.
This fixes it by detecting the nil "none" module's lack of any
prepare_frame or render_fragment methods, and open coding the blanker
with a fb_fragment_zero() inline.
|
|
These modules are meta modules, and the only place this
information is presented currently is in the rtv module captions
overlaying the visual output of unrelated modules.
So it's rather misleading to put the meta module's author and
license on-screen when what's being shown is arguably just a tiny
fraction of the meta module's contribution.
Rather than bother with constructing license and author lists at
runtime from the modules incorporated by these meta modules,
let's instead adopt a policy of meta modules omit any declaration
of license or authorship outside of the source. This is a simple
solution for now, it can be revisited later if necessary.
Changing the .author member of rototiller_module_t to an
.authors() function pointer wouldn't be difficult. But it does
open up something of a can of worms when considering recursive
dependencies and needing to construct unique authors and licenses
lists from things like nested meta modules. Obviously there
can't be infinite recursion as that would manifest in the
rendering path as well, but what I'm more concerned about is
properly handling potentialy quite long lists. It's already
annoying when rtv has to deal with a long settings string, which
I believe currently is just truncated. The same would have to be
done with long authors/licenses I guess.
In any case, I think it's probably fine to just leave authorship
and license ambiguous when a meta module is shown in rtv. It's
certainly preferable to vcaputo@pengaru.com getting credit for
everything shown in the three meta modules currently implemented,
or more specifically, the two shown in rtv; compose and montage.
Note this required making rtv tolerante of NULL .license and
.author rototiller_module_t members.
|
|
This takes a module name or "none", to use the specified module or
do nothing during the channel switching snow_duration.
The default is "snow" like before.
|
|
This renames rtv_channel_t to rtv_module_t and modules to
channels in various places, which arguably should have been in a
separate commit but I'm not up for separating that out at the
moment.
Fundamentally what's happening is every channel is getting its
own context which may persist across channel switches, this
allows watching a variety of channels in a stateful manner before
they get their contexts recreated with re-randomized settings.
For modules without settings it's not terribly interesting, and
I'm thinking modules should probably start deriving some of their
state more directly from the global ticks rather than their own
per-context counters and timers. That way even when their
contexts get recreated with re-randomized settings, there can be
some continuity for ticks-derived state. Deriving position for
instance mathematically from ticks would allow things to be
located continuously despite having their contexts and even
settings changed, which may be interesting.
Anyhow, if you want the previous behavior where contexts are
always recreated on channel switch, just set the value to be
contxt_duration equal to duration.
|
|
Ticket is unnecessarily abstract/opaque of a name for this, it's
simply the sort order. No point making the reader grok whatever
model I was thinking when I wrote it at the time, i.e. tickets at
a butcher counter.
|
|
Purely cosmetic change renaming to
rtv_should_skip_module() since the function doesn't actually
skip anything, it just determines the skip.
|
|
This adds a colon-separated channels setting, with a
setting of "all" for the existing all-modules behavior.
Colon was used since comma is already taken by the settings
separator, maybe in the future comma escaping can be added
everywhere relevant but for now just keep it simple.
The immediate value of this setting is telling rtv to limit
itself to a single module, and using its setting randomizer
to automatically observe a variety of the available settings
in action on a specific module, especially during development.
If knobs ever get added in the future I expect this will become
even more interesting for watching specific modules under their
various settings permutations in combination with their knobs
being twisted - especially if rtv reconstructs random signal
generator chains for the "knob-twisters" on every channel switch.
An immediately interesting TODO complementing this particular
change would be optionally preserving module contexts across
channel switches, so when the same module is revisited it resumes
where it was last seen. But this conflicts with settings changes
on channel switching, since contexts should probably always be
recreated when settings change - but that's probably a
module-specific detail that modules should just be robust enough
to tolerate as in they'd safely ignore settings changes without a
context recreate, or apply them if they safely can without a
context recreate... TODO.
|
|
This adds three rtv settings:
duration, caption_duration, snow_duration
|
|
Most modules find themselves wanting some kind of "t" value increasing
with time or frames rendered. It's common for them to create and
maintain this variable locally, incrementing it with every frame
rendered.
It may be interesting to introduce a global notion of ticks since
rototiller started, and have all modules derive their "t" value from
this instead of having their own private versions of it.
In future modules and general innovations it seems likely that playing
with time, like jumping it forwards and backwards to achieve some
visual effects, will be desirable. This isn't applicable to all
modules, but for many their entire visible state is derived from their
"t" value, making them entirely reversible.
This commit doesn't change any modules functionally, it only adds the
plumbing to pull a ticks value down to the modules from the core.
A ticks offset has also been introduced in preparation for supporting
dynamic shifting of the ticks value, though no API is added for doing
so yet.
It also seems likely an API will be needed for disabling the
time-based ticks advancement, with functions for explicitly setting
its value. If modules are created for incorporating external
sequencers and music coordination, they will almost certainly need to
manage the ticks value explicitly. When a sequencer jumps
forwards/backwards in the creative process, the module glue
responsible will need to keep ticks synchronized with the
sequencer/editor tool.
Before any of this can happen, we need ticks as a first-class core
thing shared by all modules.
Future commits will have to modify existing modules to use the ticks
appropriately, replacing their bespoke variants.
|
|
Switch to working through the set of modules in a random order,
randomizing the order once per cycle.
This way no modules get starved for display, which was pretty common
in the old method.
|
|
Since 7a77cc1a landed this is no longer true, the .random member
will be used to support randomizing non multiple-choice settings.
|
|
Back in the day, there was no {create,destroy}_context(), so passing
num_cpus to just prepare_frame made sense. Modules then would
implicitly initialize themselves on the first prepare_frame() call
using a static initialized variable.
Since then things have been decomposed a bit for more sophisticated
(and cleaner) modules. It can be necessary to allocate per-cpu data
structures and the natural place to do that is @ create_context(). So
this commit wires that up.
A later commit will probably have to plumb a "current cpu" identifier
into the render_fragment() function. Because a per-cpu data structure
isn't particularly useful if you can't easily address it from within
your execution context.
|
|
To facilitate random setting of these flexible string-oriented
settings, support a random helper supplied with the description.
This helper would return a valid random string to be used with the
respective setting being described.
Immediate use case is the rtv module, which also gets fixed up to
use it in this commit.
|
|
This is just a quick stab at randomizing settings, only multiple
choice setings are randomized currently.
For modules with settings, a new Settings: field is added to the
caption showing the settings as the arguments one would pass to
rototiller's module argument.
|
|
Consolidate the time() calls in setup_next_module() by using a now
variable.
|
|
This broke when snow was added.
|
|
The idea is to have captions similar to how MTV did back in the 80s.
It'd be nice to make the text resolution independent, but this is a
good first stab for an afternoon of tooling around.
|