Age | Commit message (Collapse) | Author |
|
There needs to be a way for a meta module like rocket to take
ownership of pipes immediately upon instantiation. Since the
pipes are created on demand as they become tapped by the modules
using htem, the simplest way to do this is to register some
callbacks with the ability to intercept the pipe creation in
terms of ownership and driving tap control etc.
This commit forms a minimal implementation of that, with the
ability to have a single intercepter hooked into a given stream.
It's a first-come-first-served situation, but that should suffice
for now since the rocket meta module would be the entrypoint for
such constructions. It then calls into another module to produce
on the stream, after it'll already have its hooks registered.
There might be a need for stacking hooks to let multiple modules
control pipes. GNU Rocket for instance only deals with
floats/doubles, and doesn't make it particularly easy to work on
higher order concepts like say orbiting a vector around a point
spatially. It might make sense to allow compositing of editors
where there's rocket controlling the simple floats, and another
doing dimensional/spatial stuff, with separate stacked meta
modules accomodating those editors. But that's putting the cart
before the horse, let's do the stupid simple thing for now and
see what this is like.
|
|
The driving tap's owner and pipe's owner are decoupled. When
tearing down an owner from a stream, any pipes its taps are
driving should also just go away. Otherwise its taps could
linger on pipes it doesn't own, which would be a UAF bug.
If the pipe is still needed, it'll just get recreated by another
tap. So there's a small perf hit, but this shouldn't be a
continuos kind of occurrence.
|
|
Some clarifications
|
|
|
|
When a driving tap becomes inactive, til_stream_tap() should be
able to notice and replace the driver.
An example driving tap becoming inactive would be a GNU Rocket
track that once had keys in it, but then had them all deleted.
This should set the inactive flag so the tap's automation can
take over. This gives the user at the Rocket editor the ability
to both take over from the tap automation and surrender control
back, by populating vs. emptying the respective track.
|
|
In order to implement something like a rocket module there needs
to be a way to iterate the pipes in the stream, and take
owernship of them when not already owned by rocket.
The way rocket's API works is you lookup tracks by name at
runtime. The rocket module will be a meta module that calls into
another module for rendering, arbitrarily configured via a rocket
setting a la checkers::fill_module.
So it won't be until the underlying modules do some rendering
that their taps get their respective pipes established in the
stream. Then the rocket module can look at all the pipes and for
any it doesn't own yet, it can get the tracks for those names and
take ownership while stowing the track handle in owner_foo for
the pipe.
While iterating all the pipes, the pipes already owned will have
the tracks readily available which can produce the values to
stick in the tap.
Something like that anyways, the til_stream_t api changes in this
commit are all preparatory for a rocket module.
|
|
Just clarify some verbiage, and actually assert type+n_elems
match. Note mismatch also fallsthrough to an -EINVAL just in
case asserts() have been compiled out (-DNDEBUG).
|
|
It seems likely that pipe owners will need not only a way to
differentiate themselves via the owner pointer, but also
somewhere to register a pipe-specific reference.
There probably needs to be a result pointer added for storing the
owner_foo when the owner taps, so the owner can make use of it.
|
|
We need a way to identify owners of taps when cleaning up
their containing contexts, especially once they're hanging off
streams.
|
|
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.
|
|
Preparatory commit for experimenting with a GNU Rocket
integration for controlling the stream pipes on a timeline.
Since rocket doesn't support things like arbitrary strings, it's
not a natural fit for rototiller where the obvious thing would be
to describe scene compositions as settings strings as if you were
invoking rototiller.
But a temporary hack might be to just tell a rocket module
up-front all the scenes as settings strings you provide to its
setup. Those get assigned numeric identifiers, then rocket
tracks can control when they come on/off numerically. It just
requires describing all the scenes up front rather than in the
pattern editor which is less than ideal.
Being able to experiment with this half-ass solution may prove
useful anyways, and shouldn't be too much work.
|
|
For strictly logical fragments (e.g. tiled fragmenters) there
won't be any ops, and that's even documented in the comments.
But the snapshot and reclaim functoins were assuming the ops
would be non-NULL. Snapshot in particular trips on this
assumption when a module snapshots a subfragment, like drizzle in
montage. I'm surprised I haven't encountered this crash
before...
|
|
It was assumed (n_modules - n_overlayable) would give the number
of non-overlayable modules appropriate as base layers. But with
the skipping of hermetic and experimental modules the base_idx
could be out of reach leaving layers NULL after the loop, which
will segfault later when strlen() assumes it's non-NULL.
This commit does the simple thing and also counts the unusable
modules to subtract from those eligible for base layers along
with n_overlayable.
|
|
Just some banal paperwork...
|
|
Don't make experimental modules available to the
regular/potentially-interactive setup routine.
There should be a flag like --experimental to generally enable
these.
|
|
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 only omits the modules from the random layers
Note the texture_values list is enumerated in compose_setup,
so there's no corresponding change needed there. It might make
sense to change that to a runtime-discovered list though, I think
that was done in the pre-flags era.
|
|
This allows explicit listing of such modules as channels, while
protecting the automagic/defaults scenario.
If there's a future --experimental flag or such added, then the
TIL_MODULE_EXPERIMENTAL check will have to become conditional on
it.
|
|
This turns --print-pipes into a more top-like display. Redirect
the FPS on stderr somewhere else to get less flickering
e.g. 2>/dev/null
pipes print to stdout.
|
|
TIL_MODULE_HERMETIC:
There's likely to be some new modules that are more
orchestration style components having external
runtime dependencies. Think stuff like a sequencer
talking to GNU Rocket, or something that plays back
pattern data from external files.
Those would need a GNU Rocket process to talk to
somewhere, or input pattern file paths. So they
shouldn't participate in stuff like random rtv shows
unless they have some fallbacks for when the dependencies
are unavailable. For pattern data it's realistic to
include some builtin patterns to fallback on, but we're
not there yet. So this flag when specified should opt
out of things like rtv or checkers fill_module random
selections.
TIL_MODULE_EXPERIMENTAL:
Theres no current way to have knowingly
unstable/unfinished modules available in-tree for
development/collaboration purposes without having them
also make stuff like rtv unstable. Modules having this
flag set should be excluded from random inclusion without
a --experimental or some such runtime flag specified.
This commit only assigns values and names for the flags, it
doesn't implement anything.
|
|
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).
|
|
This is a rudimentary integration of the new til_stream_t into
rototiller. If the stream is going to continue living in
til_fb_fragment_t, the fragmenters and other nested frame
scenarios likely need to be updated to copy the stream through to
make the pipes available to the nested renders.
--print-pipes dumps the values found at the pipes' driver taps
to stdout on every frame.
Right now there's no way to externally write these values, but
with --print-pipes you can already see where things are going and
it's a nice visibility tool for tapped variables in modules.
Only stars and plato tap variables presently, but that will
improve.
|
|
With --print-pipes there will be a potential shitload of stuff
getting printed out, and it'd be nice to easily distinguish that
content from the FPS counter.
Since stderr is normally less buffered than stdout (line
buffered) not lose debugging information, just put the
low-bandwidth periodic FPS print there instead. This leaves
stdout for --print-pipes output which occurs every frame *and*
may have a lot of content per print.
|
|
Particularly for simple boolean args it's desirable to just
access their values directly in the args without any further
cooking required. Rather than pointlessly duplicating those
cases, just give visibility into the raw args.
|
|
Now that til_stream_t is implemented, let's wire up the taps.
Note that nothing actually creates the stream and puts it in the
fragment yet, so stream is still always NULL for these
effectively turning this into a NOP.
|
|
Until now there's just been a forward declared type for
til_fb_fragment_t.stream's type, and it's been completely unused.
The purpose of the stream is to provide a continous inter-frame
object where information can be stored pertaining to the stream
of frames.
Right now, that information is limited to emergent "pipes" formed
by using taps against a given stream. Taps at new paths in the
stream become added as pipes for those paths, with the
responsible tap hooked in as the driving tap. Taps at existing
paths become diverted to the driving taps, enabling potential for
external drivers for tapped variables.
This commit only adds the implementation, nothing is actually
using it yet.
|
|
The purpose of the context path is to aid in locating the context
instance. The initial application of this will be in service of
the taps, which require their module's path as a sort of
containing directory of the tap name. It'd be convenient to
simply add the path hash with the tap hash to produce the tap's
"absolute path" hash when looking up in the hash table.
|
|
The purpose of the tap is ultimately to be indexed by name so
it's discoverable. I'm leaning towards using a hash table for
that, and it'd be silly to keep recomputing the hash of an
unchanging name.
|
|
Presently just for hashing paths and names
|
|
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
|
|
Wiring up some minimal taps to see how this will work...
This only initializes the taps and changes the render to access
the rates indirectly via the tapped pointers.
|
|
Wiring up some minimal taps to see how this will work...
This only initializes the taps and changes the render to access
the rates indirectly via the tapped pointers.
|
|
this is slightly more ergonomic by having one less pointer to get in
the right place in the parameters
|
|
The previous commit implemented taps in a way requiring
allocations and cleanup. Let's experiment a bit and just make
them absolutely minimal named typed variable+indirection
bindings.
The helpers are retained, but converted to initializers rather
than new() style constructors. They provide type-checking of the
variable and indirection pointer, and prevent incorrect TIL_TYPE
values going in til_type_t.type for the supplied bound
elems+ptr.
|
|
The idea here is for modules to bind variables to names @ context
create time w/til_tap_new(). Pseudo-code sample:
```
typedef struct foo_context_t {
struct {
til_tap_t *position;
} taps;
struct {
v2f_t position;
} vars;
v2f_t *position;
} foo_context_t;
foo_context_t * foo_create_context(void)
{
foo_context_t *foo = malloc(sizeof(foo_context_t));
/* This creates an isolated (pipe-)tap binding our local position variable and pointer
* to a name for later "tapping" onto a stream.
*/
foo->taps.position = til_tap_new_v2f(&foo->position, "position", 1, &foo->vars.position);
return foo;
}
foo_render(foo_context_t *foo, til_fb_fragment_t *fragment)
{
if (!til_stream_tap(fragment->stream, &foo->pipes.position)) {
/* got nothing, we're driving */
foo->position->x = cosf(ticks);
foo->position->y = sinf(ticks);
} /* else { got something, just use foo->position as-is */
draw_stuff_using_position(foo->position);
}
```
Note til_stream_tap() doesn't exist yet, this commit only adds
the tap (til_tap_new()).
The stream will probably implement a hash table for looking up
the tap by name, verifying its type and nelems match if found,
and update the pointer to point at the instance actually driving
for the name. (in the example that's the foo_context_t.position
pointer which draw_stuff_using_position() then dereferences)
Also note that in the example, "position" alone is too simplistic
for handling complex real-life compositions where a given module
may recur in a given stream. That identifier would need to be
derived from the module's context/setup producing a distinctly
unique path to the tap. i.e.
"/compose/layers/checkers/fill_module/foo:position" or something,
to be dynamically generated. And the foo:position syntax isn't
set in stone either. Maybe foo/position would suffice, the whole
heirarchical syntax needs to be thought through and defined yet.
Since the absolute path to the tap would be setup-dependent, there
will have to be some glue tying together the setup used by the
context and the tap within that context. The stream may be the
natural place where that occurs.
This also currently is barebones in terms of the tap types
supported. The only higher-order types are rudimentary 2-4d
vectors and 4x4 matrices. There are no semantics associated
with the types, and it's likely in the future either the tap
types themselves will expand to be semantic. Think things like
a camera type, composed both a point and direction vector.
As-is the few higher-order types in til_tap.h are simply forward
declared, and at least in terms of the taps alone further type
visibility isn't necessary.
It may make more sense to build upon these bare taps with another
semantic layer bringing the higher-order types to the table in a
more concrete form. All those higher-order types would then be
composed from the bare taps.
There's some conceptual overlap with the knobs stubbed out in
til_knobs.h as well. I think this likely at least partially
replaces what's there, and what it doesn't will probably end up
somewhere else.
|
|
This adds an optional stream member and type for introducing some
persistent potentially cross-fragment state bound to the logical
frame being rendered to by the fragment.
It's a preparatory commit for adding things like arbitrary
stream-bound state modules can create/read/modify for passing
non-pixel information between modules bound to the fragment's
frame that may vary frame to frame, but may also only be updated
occasionally within a stream while still being accessible by
every frame for the lifetime of the stream.
This may evolve to encompass the fragment's texture member,
turning the texture into just another arbitrary thingy tied to
the stream which modules/rendering primitives may lookup and make
use of if present.
This commit only adds the type and member though, no
implementation yet.
|
|
The existing code only really cared about preserving the incoming
texture on behalf of the caller when the compose code itself was
doing something with the texture.
But there's scenarios where the underlying module being rendered
(or its descendants) might play with the texture, and in such a
situation when the outer compose wasn't involving a texture it'd
let the descandants installed texture leak out to the callers
making the texture apply more than one'd expect.
Arguably none of the modules should be missing restoration of the
incoming texture after installing+rendering with their own. But
with this change in place, compose will clean up after nested
modules leaking their texture up.
|
|
At low framerates, the strobe module timer can expire from frame to
frame. This has the effect of appearing "always on." This condition was
obscuring output in compose mode.
#BirdwalkCommit #WinterStormWarning
|
|
Mechanical rearrangement, but ultimately there probably needs to
be an initialize function added to til_module_t. With all the
threading chaos going on, this approach to implicit
initialization with a static flag is racy without using atomics.
For now it's probably marginally better to do this in context
create vs. prepare frame. Context creates *tend* to happen in
single-threaded phases of execution, and infrequently. Prepare
frame is a serialized phase of the rendering for a given context,
but there can be many contexts in-flight simultaneously now with
all the forms of compositing happening, sometimes from multiple
threads. So that assumption no longer holds...
|
|
This gets rid of the static accumulator hack used for the blinds
phase.
|
|
There's still a handful of modules doing ad-hoc radians
accumulation in a static variable by simply adding a small value
like .01 every render.
This worked OK in the early days when 1. no rototiller instance
was ever run long enough for that accumulator to become a large
value where floating point precision started rearing its ugly
head. and 2. rototiller never really drew the same module
multiple times in compositing a frame.
Now that rototiller can produce some rather interesting outputs
the first assumption isn't really true - I've fixed memory leaks
to enable long-running sessions, so these potential precision
problems should get dealth with.
And with rtv+compose/checkers+fill_module it's quite common to
have a module rendering things many times in a single frame. So
that previously tolerable laziness of using a static accumulator
is no longer acceptable, since every invocation of the module's
renderer would bump the accumulator. When you have something
like checkers using blinds for the filler, every individual cell
is unintentionally advancing the blinds when they're intended to
be at the same phase.
So this helper is being added to conveniently turn ticks into
something you'd pass directly into cosf/sinf without worrying
about precision issues.
Future commits will start bringing modules over to use this
helper instead of whatever they're doing with static variables
or in-context accumulators etc.
There's also another reason to prefer deriving "T" from ticks
on every frame; we can do things like fast-forward/rewind effects
on modules by manipulating the ticks input value. If the modules
are accumulating this state privately, manipulating ticks won't
have the intended effect. Of course not all modules are amenable
to this kind of thing, stuff like swarm and sparkler where they
do a sort of simulation contain a pile of state that isn't
ticks-derived on every frame and can't really be converted to do
so.
|
|
After reading about the Dreamachine[0], I wanted to experience
this phenomenon. The javascript-based web implementations
struggled to hold a steady 10Hz rate and would flicker like
crazy, so here we are.
Only setting right now is period=float_seconds, defaults to .1
for 10Hz.
One limitation in the current implementation is when the frame
rate can't keep up with the period the strobe will just stick on
without ever going off, because the period will always be
expired. There should probably be a setting to force turning off
for at least one frame when it can't keep up.
[0] https://en.wikipedia.org/wiki/Dreamachine
|
|
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.
|
|
Just looking to spice up plato a bit. It seems to make a lot of
appearances in rtv, or is just one of those highly visible things
when it's participating. Way too monotonous as-is.
|
|
moire makes for an interesting texture, esp. mixed with itself:
--seed=0x62f00a7b --module=compose,layers=julia:moire:drizzle,texture=moire
|
|
Show the info, but skip the wait step.
|
|
this introduces a style= setting with values:
style=mask simple alpha mask overlay
style=map displacement mapped overlay
I might add a lighting option for the style=map mode with a
moving light source or something like that, but it's already
pretty slow as-is.
This is mostly just for more testing of the snapshotting, but
there's some interesting compositions enabled like:
module=compose,layers=submit:moire:drizzle
or just moire:drizzle, when style=map happens.
|
|
This arguably doesn't require snapshotting to work, since it's
doing a rudimentary in-place single pixel alpha-blended
replacement using a single pixel at the same location.
But the moment it startus using multiple adjacent super-samples,
the snapshot becomes necessary. If it gets further complicated
with displacements and maybe bump-mapping or something, then the
samples can become quite distant from the pixel being written,
spreading out into neighboring fragments being rendered
simultaneously etc. These are all cause for snapshotting...
For now though it's a very simple implementation that at least
makes drizzle overlayable, while also providing a smoke test for
the new snapshotting functionality.
|
|
It seems like it might be most ergonomic and convenient for
everything to just use til_fb_fragment_t and rely on ops.submit
to determine if the fragment is a page or not, and if it is how
to submit it.
This commit brings things into that state of the world, it feels
kind of gross at the til_fb_page_*() API. See the large comment
in til_fb.c added by this commit for more information.
I'm probably going to just run with this for now, it can always
get cleaned up later. What's important is to get the general
snapshotting concept and functionality in place so modules can
make use of it. There will always be things to cleanup in this
messy tangle of a program.
|
|
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.
|