Age | Commit message (Collapse) | Author |
|
Let's make it so til_module_context_t as returned from
til_module_context_new() can immediately be freed via
til_module_context_free().
Previously it was only after the context propagated out to
til_module_context_create() that it could be freed that way, as
that was where the module member was being assigned.
With this change, and wiring up the module pointer into
til_module_t.create_context() as well for convenient providing to
til_module_context_new(), til_module_t.create_context() error
paths can easily cleanup via `return til_module_context_free()`
But this does require the til_module_t.destroy_context() be able
to safely handle partially constructed contexts, since the
mid-create failure freeing won't necessarily have all the members
initialized. There will probably be some NULL derefs to fix up,
but at least the contexts are zero-initialized @ new.
|
|
As with the other composite modules, if --experimental happens
this will need adjustment to honor it.
For now let's just prevent things from breaking when those
modules start appearing.
|
|
This was mostly done out of convenience at the expense of turning
the fragment struct into more of a junk drawer.
But properly cleaning up owned stream pipes on context destroy
makes the inappropriateness of being part of til_fb_fragment_t
glaringly apparent.
Now the stream is just a separate thing passed to context create,
with a reference kept in the context for use throughout. Cleanup
of the owned pipes on the stream supplied to context create is
automagic when the context gets destroyed.
Note that despite there being a stream in the module context, the
stream to use is still supplied to all the rendering family
functions (prepare/render/finish) and it's the passed-in stream
which should be used by these functions. This is done to support
the possibility of switching out the stream frame-to-frame, which
may be interesting. Imagine doing things like a latent stream
and a future stream and switching between them on the fly for
instance. If there's a sequencing composite module, it could
flip between multiple sets of tracks or jump around multiple
streams with the visuals immediately flipping accordingly.
This should fix the --print-pipes crashing issues caused by lack
of cleanup when contexts were removed (like rtv does so often).
|
|
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
|
|
Until now when fragmenting with a texture present the texture
pointer was simply copied through to the new logical fragment.
The problem with that is when sampling pixels from the texture in
a nested frame scenario, the locations didn't align with the
placement of the logical fragment.
With this change when the incoming fragment has a texture, the
output fragment gets some uninitialized memory attached in the
outgoing fragment's texture pointer. Then the fragmenter is
expected to do the same populating of res_fragment->texture it
already did for res_fragment, just relative to
fragment->texture->{buf,stride,pitch} etc.
It's a bit hairy/janky because til_fb_fragment_t.texture is just
a pointer to another til_fb_fragment_t. So the ephemeral/logical
fragments fragmenting/tiling produces which tend to just be
sitting on the stack need to get another til_fb_fragment_t
instance somewhere and made available at the ephemeral
til_fb_fragment_t's .texture member. We don't want to be
allocating and freeing these things constantly, so for now I'm
just ad-hoc stowing the pointer of an adjacent on-stack texture
fragment in the .texture member when the incoming fragment has a
texture. But this is gross because the rest of the fragment
contents don't get initialized _at_all_, and currently if the
incoming fragment has no texture the res_fragment->texture member
isn't even initialized. The fragmenters aren't really supposed
to be expecting anything sensible in *res_fragment, but now we're
making use of res_fragment->texture *if* fragment->texture is
set. This is just gross.
So there's a bunch of asserts sprinkled around to help police
this fragility for now, but if someone writes new fragmenters
there's a good chance this will trip them up.
|
|
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.
|
|
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.
|
|
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.
|
|
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
Originally it seemed sensible to make these units of bytes, for
flexibility reasons.
But it's advantageous for everything to be able to assume pixels
are always 4-byte/32-bit aligned. Having the stride/pitch be in
bytes of units made it theoretically possible to produce
unaligned rows of pixels, which would break that assumption.
I don't think anything was ever actually producing such things,
and I've added some asserts to the {sdl,drm}_fb.c page
acquisition code to go fatal on such pages.
This change required going through all the modules and get rid of
their uint32_t vs. void* dances and other such 1-byte vs. 4-byte
scaling arithmetic.
Code is simpler now, and probably faster in some cases. And now
allows future work to just assume things cna always occur 4-bytes
at a time without concern for unaligned accesses.
|
|
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.
|
|
Instead of always showing defaults, randomize the setup like rtv
does.
|
|
There's been a longstanding todo item in montage where it was
ignoring the fragmenter returned by a module's prepare_frame().
This commit continues with the single-threaded rendering of the
modules within their respective tiles, still ad-hoc open coded.
But now actually applies the fragmenter returned as if the
rendering were being threaded, since when a module returns a
fragmenter from its prepare_frame() it may strongly depend on
that fragmenting for its output.
|
|
Mechanical renaming of "zero" to "clear" throughout for this
context.
|
|
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
|
|
Mechanically replaced ad-hoc til_module_t.destroy_context()
invocations with helper calls.
|
|
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_.
|
|
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.
|
|
The threaded rendering backend isn't reentrant and compose could
hypothetically have montage as a layer triggering infinite
recursion.
For now use a big hammer and block compose module from montage.
|
|
Once upon a time montage had to skip stars and pixbouce because
they crashed. That has since been corrected, but the commit which
removed the skips didn't remove the lookups.
This removes the leftover lookups.
|
|
This module needs some love, but it already always supplies 1 to
the open-coded rendering for the tiles. It never should have been
supplying the real num_cpus to module.create_context(), then only
supplying 1 for rendering.
|
|
This adds a bit flag for tracking if the fragment has been zeroed
since its last flip/present.
When a fresh frame comes back from flipping, its zeroed state is
reset to false.
When fb_fragment_zero() is called, it checks if zeroed is true,
and skips the zeroing if so.
If zeroed is false, fb_fragment_zero() will zero the fragment and
set the zeroed flag to 1.
This change is preparatory for layering the output of modules in
a compositing fashion. Not all modules are amenable to being
used as upper layers, since they inherently fill the screen with
new pixels every frame. Those modules make good bottom or bg
layers. Other modules perform fb_fragment_zero() every frame
and add relatively few pixels atop a clean slate. These modules
make good candidates for upper layers where this change becomes
relevant.
|
|
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.
|
|
Ideally the number of modules can tile without leaving gaps, but
as rototiller evolves over time modules are added piecemeal so
try accomodate those awkward layouts.
|
|
|
|
While the montage doesn't deeply thread the per-tile/module rendering,
the per-frame rendering is threaded with a work unit granularity of
every module's tile.
Meaning every module renders its tile in a single thread, but the
tiles are all rendered in parallel.
For the most part this works, and will only work better as more modules
are added to rototiller increasing the granularity. In the mean time
it's a bit coarse and some modules can be a lot more costly to render
than others, and there can be a shortage of modules to schedule on idle
CPUs.
It would be an interesting task to try make each module's tile get
subfragmented elastically. I didn't make any attempt to do that, but
it might even be worthwhile on hidpi screens where even those small
tiles may have a whole lot of pixels, especially on manycore CPUs.
|
|
This repurposes the generic fb_fragment_tile_single() to better fit
the montage use case.
Partially covered areas are simply skipped, and tiles no longer need
to be square.
In determining the tile width and height, I'm just using the sqrt of
the number of modules and dividing the frame width and height. But
when the sqrt has a fraction, it's rounded up on the width divide
and rounded down on the height divide. So the width gets the extra
column of tiles, and the height just throws away the fraction.
I think it's OK for now, until someone gets a bug up their ass and
wants to avoid having vacant tiles in the bottom right corner when
the number of modules doesn't cooperate.
One problem with the just skipping partially covered areas is they
don't get zeroed out - no fragment is ever generated for them.
To fix that there will prolly have to be a fb_fragment_zero() of
the frame @ prepare :(. I guess it wouldn't be the end of the
world if the fragmenter itself zeroed out skipped regions. It's
kind of an ugly layering violation but this is a private montage-
specific fragmenter.
|
|
The old approach was just to get things working, it's preferable
to not have empty tiles on-screen where modules were skipped and
have all tiles be smaller to accomodate vacancies.
Now the modules list gets pruned @ context create, so the skipping
only happens once and everywhere else is looking at a modules list
and count of only the keepers.
I also added stars to the skipped modules, for now, since both stars
and pixbounce malfunction when the fragment size changes.
|
|
Not doing this produces especially visible artifacts when shown
by rtv.
|
|
Segfaults were observed when montage came up in rtv, since pixbounce
doesn't seem to be rendering properly at all just skip it for now.
I suspect what's happening is rtv ran pixbounce before running montage,
and pixbounce caches fragment knowledge @ initialization. So when
montage ran pixbounce in a tile, that stale fragment knowledge was used
and caused scribbling.
Stars probably has similar problems actually.
|
|
This is somewhat unfinished as it uses the generic tiled fragmenter
that's not interested in appearances but prioritizes total coverage
and simplicity.
Montage should have its own tiler that can produce non-square and even
non-uniform tile dimensions, prioritizing filling the screen with
mostly-uniform tiles.
But that's a TODO item, this is good enough for now and exercises some
fragment details previously irrelevant and often ignored/broken in
modules.
The pixbounce module in particular seems completely broken with small
fragment sizes.
|