Age | Commit message (Collapse) | Author |
|
This just stubs out a rocket meta module that renders with
another module.
Future commits will integrate GNU Rocket here.
When recursive settings formally lands you'll be able to nest as
much settings content as necessary for the underlying module
used, as part of the rocket settings. That should enable
describing stuff like complex compose scenarios for rocket to
sequence.
|
|
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.
|
|
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.
|
|
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 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).
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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.
|
|
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 introduces a bespoke fragmenter for checkers. The generic
til_fb tiler isn't concerned with aesthetics so it doesn't
particularly care if clipped tiles are asymmetrically
distributed. It worked fine to get checkers developed and
working, but it's really unattractive to have the whole be
off-centered when the checkers don't perfectly align with the
frame size.
There's some gross aspects like leaving the frame_{width,height}
to be corrected at render time so render_fragment can access the
incoming frame_width for cell state determination.
|
|
quick and dirty fixup for proper use as checkers fill_module=
note this thing already relies on _checked() put_pixel variant
|
|
s/unchecked/checked/ and use frame dimensions, probably more fixes
needed but this prevents crashing as checkers fill_module= at least.
|
|
Sorry but more s/unchecked/checked/ and now this seems to not
crash when used as a checkers fill_module.
|
|
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.
|
|
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.
|
|
Wire up seed via til_module_context.seed in the obvious manner,
minimal change to the code, no functional difference besides
giving pixbounces instances an isolated random seed state.
|
|
Normalized all the randomizers to use til_module_context.seed
while in here.
|
|
wired up to til_module_context.seed
|
|
This is a little contorted but not too bad. The input to
particles_new() is just a const conf struct, so instead of
passing in the seed value for particles_t to contain, a pointer
to where the seed lives is passed in via the conf. This requires
the caller to persist a seed somewhere outside the particles
instance, but at least in rototiller we already have that
conveniently in til_module_context_t.
|
|
More obvious migrations to using the supplied seed
|
|
also update call sites in modules/{meta2d,swab} accordingly
|
|
More plumbing seed to rand in the obvious way...
|
|
Just plumbing seed down in the obvoius manner, this could
probably be cleaned up a bit in the future.
|
|
Due to how the values get matched as strings this extraneous 0
was interfering with FLUI2D_DEFAULT_CLOCKSTEP actually matching
anything.
|
|
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.
|
|
Once upon a time this thing asserted the pinned chunks were
empty, and that code is still sitting there commented out. But
when it was commented out ages ago, to enable bulk freeing of
chunkers without requiring unrefs of every allocation as an
optimization, no code was added to free the pinned chunks.
It's been easily ignored all this time since nobody really runs
rototiller long enough to notice, but that's becoming less true
now with how interesting something like:
--module=rtv,layers=compose,duration=1,context_duration=1,snow_module=none
is becoming...
There are other leaks still, largely surrounding settings, but
they are quite small. Eventually those will get tidied up as
well.
|
|
this introduces a color= setting syntax:
color=#rrggbb
color=0xrrggbb
color=rrggbb
where rrggbb is case-insensitive html-style hexadecimal
also introduces a fill= setting:
fill=color
fill=sampled
fill=textured
fill=random
fill=mixed
sampled draws the color from the incoming fragment when layered,
textured draws the pixels from the texture when available,
random randomizes the choice from color,sampled,textured.
mixed isn't implemented fully and is just aliased to random
currently. The thinking for mixed is to allow specifying
proportions for color,sampled,textured which would then be
applied as weights when randomizing the selection from the three
at every filled checker.
the current implementation is just calling rand() when
randomized, but should really be like the other dynamics in
checkers with rate control and hash-based.
and introduces a fill_module= setting:
this is a first stab at employing other modules for filling the
filled cells.
Note since checkers is already a threaded module, the fill module
context gets created per-cpu but with an n_cpus=1.
This is kind of the first time module contexts are being rendered
manifold for the same frame, and that's illuminating some
shortcomings which needed to be dealt with. Some modules
automatically advance a phase/T value on every render which gets
persisted in their context struct. With how checkers is using
contexts, it's desirable for multiple renders of the same context
using the same ticks to produce the same output. So modules need
to be more careful about time and determine "dt" (delta-time)
values, and animate proportional to ticks elapsed. When ticks
doesn't change between renders, dt is zero, and nothing should
change.
For now this is using a hard-coded list of modules to choose
from, you specify the module by name or "none" for no
fill_module (solid checker fill). ex: "fill_module=shapes"
There's a need for something like fragment color and flag
overrides to allow til_module_render() to be treated as more of a
brush where the caller gets to specify what colors to use, or if
texturing should be allowed. For now, when fill_module=$module
is employed, the color determination stuff within checkers
doesn't get applied. That will need to be fixed in the future.
|
|
modules/checkers w/fill_module=$module requires a consistent
mapping of cpu to fragnum since it creates a per-cpu
til_module_context_t for the fill_module.
The existing implementation for threaded rendering maximizes
performance by letting *any* scheduled to run thread advance
fragnum atomically and render the acquired fragnum
indiscriminately. A side effect of this is any given frame, even
rendered by the same module, will have a random mapping of
cpus/threads to fragnums.
With this change, the simple til_module_t.prepare_frame() API of
returning a bare fragmenter function is changed to instead return
a "frame plan" in til_frame_plan_t. Right now til_frame_plan_t
just contains the same fragmenter as before, but also has a
.cpu_affinity member for setting if the frame requires a stable
relationship of cpu/thread to fragnum.
Setting .cpu_affinity should be avoided if unnecessary, and that
is the default if you don't mention .cpu_affinity at all when
initializing the plan in the ergonomic manner w/designated
initializers. This is because the way .cpu_affinity is
implemented will leave threads spinning while they poll for
*their* next fragnum using atomic intrinsics. There's probably
some room for improvement here, but this is good enough for now
to get things working and correct.
|
|
The palette mutates across frames, on a context-specific
schedule. Meaning the palette is per-context, so move it into
roto_context_t.
The phase also needs to be driven by ticks. And when ticks
doesn't change in cases where the same context is rendered
manifold in the same frame, the phase shouldn't move.
|
|
This way if a given context gets rendered repeatedly for the same
tick, no movement occurs, until ticks changes.
|
|
Also wire this up to the til_module_context_new() helper and
all its callers.
This is in preparation for modules doing more correct delta-T
derived animation.
|
|
While testing an experimental checkers w/fill_module=blinds with
ASAN it became clear this module is making flawed assumptions
about fragment->frame_{width,height} and fragment->{width,height}
being equal.
When used by checkers for filling cells, there are situations
where the edge cell fragments need to describe a frame slightly
larger than the drawn area, because the cell size doesn't align
perfectly to the overall window/screen dimensions. So in these
cases the synthesized frame will still be a full cell's
dimensions while the width,height serve to clip within that area.
If modules aren't properly clipping their rendering, instead just
using frame_{width,height}, then they will have to use the
_checked() variants to ensure clipping occurs properly on a
per-pixel (slower) basis.
|
|
Contexts aren't void* anymore, and free the contexts array too on
failure.
|
|
This initializer could perform an out-of-bounds read since it
occurs before the n_modules bounds check.
Since the variable isn't even being used anymore just get rid of
this. Also found via ASAN.
|