Age | Commit message (Collapse) | Author |
|
Modules can now use the til_module_t.finish_frame() return value
to trigger re-rendering by returning 1, returning 0 finishes the
frame.
A smattering of til_module_t.finish_frame() implementations were
largely mechanically updated to match this change by returning 0,
since nothing actually uses multi-pass rendering yet.
The impetus for this is experimenting with the flow module doing
two passes of threaded rendering per frame. A first pass to
sample the flow field and update the elements, per-cpu, but
drawing nothing. Then a second pass to render the elements in a
tiled manner.
|
|
More setup_func conversion to returning the failed setting on
errors during res_setup baking.
|
|
It might be worthwhile to support >1 for these, but as-is it
crashes due to assumptions WRT shape size not exceeding frame
dimensions.
This just prevents crashes if someone tries putting a >1 value in
something like a rkt track for scale/pinch_factor.
|
|
First stab at tapping the parameterized stuff in shapes.
|
|
This also makes the pinches-dependent settings always get
initialized, since when tapped if you moved n_pinches from 0 to
non-zero, the pinches-dependent settings would abruptly become
relevant mid-flight. So this ensures they can have some sane
values, in case they haven't also been set via taps when
n_pinches became non-zero.
|
|
Preparatory commit for potential n_pinches=0, sin(0)=0 which
behaves better here where n_pinches=0 should be a noop.
|
|
Threaded rendering was added in 011d4df35
|
|
b6362c54 introduced shapes_destroy_context() for the radcache,
but didn't actually cleanup the context, oops.
trivial fix
|
|
Running something like:
'--module=checkers,size=64,fill_module=shapes,style=circle --defaults --go'
Would show some artifacts in the top portion of the filled cells
at certain phases of the spining shape. This was introduce by
the radcache, and it's caused by the overhanging tiles on the
perimeter and how their being clipped was carrying into the
radcache contents then used for all subsequent renders.
This commit just makes setting radcache.initialized conditional
on the incoming fragment being a full-frame fragment as a quick
fix. See comment for more info on what to do long-term.
|
|
Particularly with nested modules it's annoying to have to stow
the module separate from the setup during the setup process.
If the baked setup included the module pointer in the
non-module-specific-setup part of the setup, then nested settings
could finalize using the generic module setup wrapper and just
rely on this til_setup_t.creator pointer to contain the
appropriate module. Which should enable tossing out a bunch of
copy-n-pasta surrounding nested modules setup.
Note this has to be a void* since til_setup_t is a generic thing
used equally by both the fb code and the module code. Hence why
this is called "creator" and not "module", as well as the void*
as opposed to til_module_t*.
Also if rototiller ever grows a sound backend, the setup
machinery will be reused there as well, and it'll be yet another
creator handle that isn't an til_fb_ops_t or a til_module_t.
It's assumed that the callers producing these setups won't be
trying to pass them to the wrong places i.e. a module setup
getting passed to an fb backend and vice versa.
I'm mildly annoyed about having to move the various til_module_t
blocks to above the module's foo_setup(), but it seemed like the
least annoying option. This may be revisited.
|
|
Mechanical change switching til_fragmenter_slice_per_cpu() users
over to til_fragmenter_slice_per_cpu_x16(), except sparkler where
it's quite detrimental to performance.
|
|
With the addition of the "radcache" in b6362c, the need for a
faster approximate atan2f() is largely eliminated.
And there seems to be a bug in the implementation as-is taken
from https://mazzo.li/posts/vectorized-atan2.html
You can see the bug as vertical line artifact around the center
where the X coordinate would be 0. Rather than debug what's
wrong with this approximation's implementation surrounding its
quadrant adjustments, let's just resume using atan2f() and let
the cache keep things quick.
|
|
computing the angle for every pixel coordinate from the origin is
costly, even with the approximate method added by c690303.
An easy speedup is to only do this once for a given frame
dimensions, and cache those results. In the form of a 32-bit
float, it's equivalent to caching a full page of pixel data.
This is slightly complicated by needing to be an effectively
global cache and the potential for multiple shapes contexts
rendering concurrently when part of a composition.
I think this particular situation highlights a need for something
equivalent generalized on-stream where modules can register
discoverable caches of costly to compute information, having a
high probability of being useful to others.
In this particular case it was alphazed's use of shapes in two
layers that made it an obvious win, even without any other
modules needing atan2() per-pixel with a centered origin.
With this commit:
Configured settings as flags:
--seed=0x64adabae '--module=compose,layers=blank\,shapes\\\,type\\\=pinwheel\\\,scale\\\=.9\\\,pinch\\\=.25\\\,pinch_spin\\\=.1\\\,pinches\\\=7\\\,points\\\=19\\\,spin\\\=.01\,shapes\\\,type\\\=pinwheel\\\,scale\\\=.9\\\,pinch\\\=1\\\,pinch_spin\\\=-.25\\\,pinches\\\=8\\\,points\\\=5\\\,spin\\\=0,texture=moire\,centers\=3' '--video=mem,size=1366x768'
FPS: 73
FPS: 74
FPS: 73
Without:
Configured settings as flags:
--seed=0x64adb857 '--module=compose,layers=blank\,shapes\\\,type\\\=pinwheel\\\,scale\\\=.9\\\,pinch\\\=.25\\\,pinch_spin\\\=.1\\\,pinches\\\=7\\\,points\\\=19\\\,spin\\\=.01\,shapes\\\,type\\\=pinwheel\\\,scale\\\=.9\\\,pinch\\\=1\\\,pinch_spin\\\=-.25\\\,pinches\\\=8\\\,points\\\=5\\\,spin\\\=0,texture=moire\,centers\=3' '--video=mem,size=1366x768'
FPS: 55
FPS: 54
FPS: 54
So it's significant, and in alphazed there's also a transition
from one scene with two full-screen shapes layers into a
checkered scene with shapes as the fill_module. Further
amplifying the payoff.. infact whenever shapes is used for a
fill_module in checkers, there's n_cpus shapes contexts created
because checkers is threaded. All of those would be benefitting
from the cache.
|
|
While here also did more minor optimizations moving things out of
the inner loops that were only there out of laziness...
|
|
Taken from this excellent post:
https://mazzo.li/posts/vectorized-atan2.html
While I haven't gone full vectorized, just getting rid of the
regular atan2f() call will be a big improvement.
This just adds the functionality, nothing is calling it yet.
|
|
This is just a first stab at threading shapes... whenever shapes
finds itself in a scene it easily becomes a significant
bottleneck, threading is a trivial path to improving that
somewhat.
While at it I also got rid of the need for _checked() variants
which also helps a bit.
But nothing here is proper optimization of the routines.. there's
too much math happening per pixel in a naive fashion.
|
|
This changes til_setup_t* from optional to required for
til_module_context_t creation, while dropping the separate path
parameter construction and passing throughout.
|
|
This commit adds passing the settings instance to til_setup_new()
which is used for deriving a path for the setup via
til_settings_print_path() on the supplied settings.
That path gets an allocated copy left in the returned
til_setup_t at til_setup_t.path
This path will exist for the lifetime of the til_setup_t, to be
freed along with the rest of the baked setup instance when the
refcount reaches 0.
The incoming til_settings_t is only read @ til_setup_new() in
constructing the path, no reference is kept. Basically the
til_settings_t* is just passed in for convenience reasons, since
constructing the path needs memory and may fail, this approach
lets the existing til_setup_new() call error handling also
capture the path allocation failures as-is turning
til_setup_new() into a bit more of a convenience helper.
Note that now all code may assume a til_setup_t has a set and
valid til_setup_t.path, which should be useful for context
creates when a setup is available.
|
|
Commit 7c8086020 switched the til_setup_new() api to support NULL
free_func for free().
This mechanical change pivots to that instead of the awkwardly
cast free() parameters.
|
|
3b6e34e70 broke this with what looks to be a silly mistake in
modifying the existing put_pixel() calls.
Kept the fragment->{x,y} instead of removing those and keeping
the bare {x,y}.
|
|
With setup refcounting and a reference bound to the context, we
should just dereference the single instance. The way setups are
used it just as a read-only thing to affect context behavior...
Note I've left the module-type-specific setup pointer despite it
duplicating the setup pointer in the module_context. This is
just a convenience thing so the accessors don't have to cast the
general til_setup_t* to my_module_setup_t* everywhere.
|
|
This just does the obvious pulling in of til_setup_t, holding the
reference throughout the lifetime of the module context.
|
|
There was a time when it made sense for context creates needing
setups but not receiving them to still be functional with some
sane defaults.
But with recursive settings, we really shouldn't ever have
orphaned nested module uses unreachable by a proper setup.
So let's just get rid of this fallback, and exclusively rely on
the baked setups provided by the .setup() methods. They still
have preferred defaults, and the proper setup production
machinery is what should be responsible for applying those
at runtime where they may also be overridden or otherwise
influenced.
|
|
For recursive settings the individual setting being described
needs to get added to a potentially different settings instance
than the one being operated on at the top of the current
setup_func phase.
The settings instance being passed around for a setup_func to
operate on is constified, mainly to try ensure modules don't
start directly mucking with the settings. They're supposed to
just describe what they want next and iterate back and forth,
with the front-end creating the settings from the returned descs
however is appropriate, eventually building up the settings to
completion.
But since it's the setup_func that decides which settings
instance is appropriate for containing the setting.. at some
point it must associate a settings instance with the desc it's
producing, one that is going to be necessarily written to.
So here I'm just turning the existing til_setting_desc_t to a
"spec", unchanged. And introducing a new til_setting_desc_t
embedding the spec, accompanied by a non-const til_settings_t*
"container".
Now what setup_funcs use to express settings are a spec,
otherwise identically to before. Instead of cloning a desc to
allocate it for returning to the front-end, the desc is created
from a spec with the target settings instance passed in.
This turns the desc step where we take a constified settings
instance and cast it into a non-const a more formal act of going
from spec->desc, binding the spec to a specific settings
instance. It will also serve to isolate that hacky cast to a
til_settings function, and all the accessors of
til_setting_desc_t needing to operate on the containing settings
instance can just do so.
As of this commit, the container pointer is just sitting in the
desc_t but isn't being made use of or even assigned yet. This is
just to minimize the amount of churn happening in this otherwise
mostly mechanical and sprawling commit.
There's also been some small changes surrounding the desc
generators and plumbing of the settings instance where there
previously wasn't any. It's unclear to me if desc generators
will stay desc generators or turn into spec generators. For now
those are mostly just used by the drm_fb stuff anyways, modules
haven't made use of them, so they can stay a little crufty
harmlessly for now.
|
|
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.
|
|
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
|
|
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.
|
|
This is a first approximation of correct handling of arbitrarily
clipped frames described by the incoming fragment.
It's still relying on the _checked() put_pixel variants for
clipping. That should probably be improved by constraining the
loops to the clipped fragment edges.
|
|
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.
|
|
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/<=/</
|
|
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.
|