Age | Commit message (Collapse) | Author |
|
This opens up the possibility of referencing contexts on-stream
by-path anywhere a module may be used:
ref,path=/path/to/existing/context
Currently the path lookup is performed @ render time, and a
reference is taken on the context found there on the first time
it's located. That reference is then held in the ref's context
until its context is destroyed.
There are more details to flesh out, and what probably needs to
happen is some til_stream API for maintaining the reference in
the event that the context at that path gets replaced. Due to
refcounting, the referenced context will persist even if the
stream-resident instance goes away, but there should probably be
a way for that replacement to invalidate the existing references
so they replace theirs with a fresh lookup at the path...
Either way this is a good start. With this addition you can
effectively implement scene transitions in rkt, via the mixer
module, something like (sans proper escaping):
mixer,a_module=ref\,path=/foo/previous/ctxt,b_module=ref\,path=/foo/next/ctxt
|
|
open_memstream() isn't implemented by windows (not even mingw)
|
|
this is very early/unfinished, hence experimental flag
|
|
checkers::fill_module is presently implemented via creating
n_cpus identical module contexts, each having n_cpus=1.
Establishing a set of cloned per-cpu contexts, allowing threaded
rendering using any fill_module, regardless of if that module
internally implements threaded rendering.
This works fine, but creates some awkwardness for a future where
contexts are registered and discoverable within the stream at
their respective setup's path. In the checkers::fill_module
case, there would be n_cpus contexts at the same path since they
share the same til_setup_t. Those need to be dealt with
gracefully, ideally making the clones available to another
potentially clone-needing module (imagine a checkers::fill_module
transitionining to another checkers::fill_module situation, where
the transitioned-to fill_module refers to the context by path, it
should get all the contexts out of the stream as-is)
In this commit I'm adding a more formalized method of creating
multiple contexts from the same set of parameters, in preparation
for a future where module contexts get registered on the stream
at their til_setup_t.path. By putting the cloned creates behind
the til API it should at least be relatively trivial to get the
on-stream context registration to capture the multiple contexts.
|
|
Rather than creating an orphaned settings instance private to
til_module_setup_randomize(), the function now requires the
settings instance be provided.
The one remaining caller of this function is modules::rtv. Now
that rtv is responsible for creating the settings beforehand, and
the settings may be created with a path prefix, rtv gets its
til_module_context_t->setup.path prefixed for all the channel
settings.
Another improvement is now the channel settings instance gets
created from the module name as the settings string. So while
it's not yet possible to sparsely specify settings with others
being randomized, at least now when log_channels=on is in effect,
the printed args include the top-level channel module.
Having proper complete paths for the rtv channel modules is
especially visible in --print-paths output FYI.
An interesting test for exercising all this is:
```
$ src/rototiller --module=rtv,duration=0,context_duration=0,snow_module=none,channels=all,log_channels=on --print-pipes --defaults --go 2>/tmp/channels
in another terminal:
$ tail -F /tmp/channels
```
watch the chaos unfold
|
|
r variable is checked for zero(success) before writing to *res_arg,
but could be left uninitialized (especially in a future scenario where
an incoming til_settings_t is fully populated and described).
|
|
Preparatory commit for bridging the gap separating a baked
til_setup_t from a runtime-populated descendant til_settings_t
like modules::rtv produces for its channels via
til_module_setup_randomize().
For these currently orphaned til_settings_t instances we don't
readily have access to the logical ancestor til_settings_t that
was used to produce the module_context's bound til_setup_t. But
we don't really need the ancestor til_settings_t, all we _really_
want is the ancestral path to prefix the orphan til_settings_t
instances.
So this commit introduces supplying a prefix which gets prepended
to paths printed via the settings instance. A later commit will
make use of this in modules::rtv when producing the settings
instance passed to til_module_setup_randomize()
|
|
Preparatory commit for when settings are supplied by caller,
potentially populated and described.
|
|
In a world where "describing" settings is an iterative process,
especially post-nested-settings which are realized via the
desc-applying process, it's better to not even offer desc-setting
while adding a new setting.
This commit just gets rid of that.
The one caller that was passing a non-NULL desc to
til_settings_add_value(), til_module_setup_randomize(), was
redundantly doing so since the subsequent desc-processing was
assigning it again anyways. Future commits will likely change
til_module_setup_randomize() use a non-NULL desc for skipping
desc-applying, which wouldn't even work if it was always setting
the desc @ add time. That becomes necessary for partially
randomizing sparsely-populated settings.
|
|
There's often a need to exclude specific modules, though it's
often a hacky kludge. It's something relied upon currently for
preventing dangerous recursion scenarios, which will likely get
fixed up more robustly in the future.
|
|
In situations where modules wish to alias setting values like
expanding "all" -> "mod0,mod1,mod2,mod3" they need a way to
intercept the value-acceptance @ desc-assignment time in the
front-end. This optional override() function does just that when
present in the spec.
The current setting's value is passed to the override, and
if what's returned differs from what was passed (by pointer
value), then the current value is freed and the override takes
its place. The override function is expected to _always_ return
non-NULL; either the value provided, or a newly allocated value
override. The override function must never free the supplied
value, that's the front-end's job in applying the override.
The override() must return NULL on errors, which are assumed to
be limited to ENOMEM failures.
|
|
Composite modules that want to provide "all" aliases for modules
like rtv have to construct a comma-separated string of all module
names, usually filtered by some flags.
This helper does just that, but it does add yet another
open_memstream() user to revisit when those all get fixed up.
|
|
Mechanical rename just to make this consistent with
til_module_setup()/til_module_setup_finalize()
I should probably do a cleanup pass throughout the til APIs to
standardize on a subject-verb-object or subject-object-verb
order... Things have become a little inconsistent organically
over time
|
|
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.
|
|
Preparatory for required til_setup_t @ til_module_create_context()
This basically brings til_module_randomize_setup() inline with
til_module_setup_finalize() in that it will still produce a
minimal til_setup_t even if there is no til_module_t.setup()
method.
A future commit will do something about the orphaned
til_settings_t within til_module_randomize_setup() to get a full
path on the produced setup - which will likely simultaneously
bring us into a world where one can influence the randomized
settings externally as well. Influenced in the sense of
potentialy making some of those settings statically configured
while leaving others to be (re)randomized at the time of
til_module_randomize_setup() executing.
|
|
Preparatory commit for til_module_create_context() requiring
setups even when there's no til_module_t.setup() method.
This helper will produce the minimal til_setup_t in such cases,
or hand off the task to til_module_t.setup() when present.
Note the need for passing res_setting and res_desc to
til_module_t.setup() despite not being a settings-construction
scenario. This is because of how modules using nested settings
tend to use res_setting for storing the current setting in
accessing the nested instance, which must still occur even when
just baking the complete setup.
It's expected that any composite/meta modules utilizing other
modules will use this helper to produce the baked setups, instead
of the ad-hoc direct calling of til_module_t.setup() they do
presently.
|
|
b9123bbb added describing incoming preexisting settings, but the
spec's returned values omitted experimental modules. This
resulted in breaking the ability to say things like --module=rkt
to access the experimental rkt module despite it not showing in
the interactive setup list of values.
This commit includes experimental modules in the spec's values
when being produced for a preexisting, undescribed setting.
Restoring one's ability to access experimental modules via the
CLI.
|
|
Currently settings instances get labels from three sources:
1. explicitly labeled by a root-level til_settings_new() call,
like main.c::til_settings_new(NULL, "video", args->video);
2. implicitly labeled in a spec.as_nested_settings w/spec.key
3. positionally labeled in a spec.as_nested_settings w/o spec.key
But when constructing setting/desc paths, using strictly these
settings instance labels as the "directory name path component"
equivalent, leaves something to be desired.
Take this hypothetical module setting path for example:
/module/layers/[0]/viscosity
Strictly using settings instance labels as-is, the above is what
you'd get for the drizzle::viscosity setting in something like:
--module=compose,layers=drizzle
Which is really awkward. What's really desired is more like:
/module/compose/layers/[0]/drizzle/viscosity
Now one way to achieve that is to just create more settings
instances to hold these module names as labels and things would
Just Work more or less.
But that would be rather annoying and heavyweight, when what's
_really_ wanted is a way to turn the first entry's value of a
given setting instance into a sort of synthetic directory
component in the path.
So that's what this commit does. When a spec has .as_label
specified, it's saying that path construction should treat this
setting's value as if it were a label on a settings instance.
But it's special cased to only apply to descs hanging off the
first entry of a settings instance, as that's the only scenario
we're making use of, and it avoids having to do crazy things like
search all the entries for specs w/.as_label set.
It feels a bit janky but it does achieve what's needed with
little pain/churn.
|
|
7ff8ef94 switched things to always describe relevant settings,
but left some loose ends. Obvious fixups for those
|
|
In this "all settings require descs" world, when setup_func
forgets to return a res_desc for a setting it wants, we can end
up in an infinite loop situation. It's better to abort
immediately on the assert to catch such a program error.
|
|
Preparatory for constructing unique paths from a given
setting/settings instance by walking up the tree
|
|
Commit d72e924a introduced skipping experimental modules for the
values, but it didn't bring in a new index for storing the values
so the offset w/skips was being used to store the values too.
This created a sparse set of values for the setup, manifesting as
a wonky partial interactive setup for the module that didn't even
include the default 0 (rtv) value in the list.
Simple fix.
|
|
This just does the obvious pulling in of til_setup_t, holding the
reference throughout the lifetime of the module context.
|
|
If the module has a .setup() method, it should be able to just
blindly expect a valid baked setup to come in @ context create.
Let's enforce that here, and let the module code providing setup
hooks just go forth and access the setup assuming it's at least
present and as valid as their setup method produces.
|
|
This probably needs more work, but it's good enough for now
|
|
This commit pivots everything over to using desc->container as
the target settings instance when adding settings, as well as
actually assigning the settings container @ desc create.
Given nothing is actually triggering settings heirarchies yet (no
specs set as_nested_settings) this shouldn't actually result in
any realized functional difference, yet. The settings pointer
being placed in desc->container should be identical to what was
getting used before.
|
|
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.
|
|
The core thing here is rather than turning a bare value into a
key as I was doing before - we just leave the bare value as a
bare value and its setting must be located positionally via
get_value_by_idx since there's no key.
Existing callers that used to get_key() positionally now
get_value_by_idx() positionally all the same, except it's the
value instead of the key. This is mostly done for things like
the module or fb name at the front of a settings instance.
The impetus for this change is partially just
cosmetic/ergonomics, but it's also rather strange for what's
really a key-less value to be treated as a value-less key. It
was also awkward to talk/reason about on the road to recursive
settings where bare values would be supported as a standalone
settings instance if properly escaped...
This also adds unescaping of keys, and adds a dependency on the
somewhat linux-specific open_memstream() which may need changing
in the future (see comments).
|
|
setup_func isn't formally defined for libtil, but
setup_interactively() defacto establishes it and
til_module_t.setup() reflects the same signature and calling
convention except with til_settings_t constified.
This change makes them all consistent in this regard, but there
should probably be a formal typedef added for the function.
The reason for constifying this is I don't want setup functions
directly manipulating the settings instance. In the case of
rototiller::setup_interactively() we ensure the stdio-based
interactive setup is always the side doing the manipulation of
the settings. For a libtil-user like glimmer, it's slightly
different beast with GTK+ in the loop, but by preventing the
setup_funcs from messing directly with the settings (instead
having to describe what they want done iteratively), the
front-end always gets its opportunity to maintain its state while
doing the described things.
Of course, this is mostly a lie, and within libtil the constified
til_settings_t gets cast away to modify it in places. But
keeping that limited to within libtil is tolerable IMO. We just
don't want to see such casts in module code.
|
|
This adds a mandatory label string to til_setttings_new() and
updates call sites accordingly.
For now the root-level settings created by main.c are simply
named "module" and "video" respectively. Any nested settings
creations on behalf of modules will be labeled using the module's
name the settings are being created for use with.
This might evolve with time, for now it's just a minimum churn
kind of decision. I can see it changing such that the top-level
settings also become labeled by the module/video driver name
rather than the obtuse "module" "video" strings.
How these will be leveraged is unclear presently. At the least
it'll be nice to have a label for debugging til_settings_t
heirarchies once recursive settings support lands. In a sense
this is a preparatory commit for that work. But I could see the
labels ending up in serialization contents as markup/syntactic
sugar just to self-document things as well.
There might also be a need to address til_settings_t instances in
the settings heirarchy, which may be something like a
"label/label/label/label" path style thing - though there'd be a
need to deal with name collisions in that approach.
I'm just thinking a bit about how knobs will become addressed
when those become a real thing. The settings label heirarchy
might be the convenient place to name everything in a tree, which
knobs could then inherit their parent paths from under which
their respective knob labels will reside. For the whole name
collision issue there could just be some builtin settings keys
for overriding the automatic module name labeling, something
like:
--module=compose,layers=checkers\,label=first\,fill_module=shapes:checkers\,label=second\,fill_module=shapes
would result in:
/module/first/shapes
/module/second/shapes
or in a world where the root settings weren't just named "module"
and "video":
/compose/first/shapes
/compose/second/shapes
then if there were knobs under checkers and shapes, say checkers
had a "foo" knob and checkers had a "bar" knob, they'd be under
.knobs in each directory:
/compose/first/.knobs/foo
/compose/first/shapes/.knobs/bar
/compose/second/.knobs/foo
/compose/second/shapes/.knobs/bar
something along those lines, and of course if compose had knobs
they'd be under /compose/.knobs
This is just a brain dump and will surely all change before
implemented.
|
|
It's annoying to have the til module called rocket, and the sync
tracker protocol/library called rocket, so let's at least
differentiate it in code/comments/textual discussion.
Plus this results in shorter module context paths i.e.:
/rkt:scene
/rkt/compose/drizzle:rain
/rkt/compose/drizzle:viscosity
/rkt/compose/plato:spin_rate
/rkt/compose/plato:orbit_rate
vs.
/rocket:scene
etc...
These names are shown in the editor, and they'll tend to be long
but let's at least get the root name down to three chars this
way.
A rename of the files and build system update will come in a
subsequent commit
|
|
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.
|
|
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.
|
|
Don't make experimental modules available to the
regular/potentially-interactive setup routine.
There should be a flag like --experimental to generally enable
these.
|
|
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
|
|
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.
|
|
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 consolidates the prepare_frame+render_fragment
potentially-threaded branch but more importantly introduces some
asserts codifying the whole prepare_frame() must return a
fragmenter /and/ be accompanied by a render_fragment().
Any single-threaded modules are expected to just populate
render_fragment() and leave prepare_frame() unused.
|
|
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.
|
|
This enables reproducible yet pseudo-randomized visuals, at least
for the fully procedural modules.
The modules that are more simulation-y like sparkler and swarm
will still have runtime variations since they are dependent on
how much the simulation can run and there's been a lot of
sloppiness surrounding delta-t correctness and such.
But still, in a general sense, you'll find more or less similar
results even when doing randomized things like
module=rtv,channels=compose using the same seed value.
For the moment it only accepts a hexadecimal value, the leading
0x is optional.
e.g. these are all valid:
--seed=0xdeadbeef
--seed=0xdEAdBeFf
--seed=0x (produces 0)
--seed=0xff
--seed=deadbeef
--seed=ff
--seed= (produces 0)
--seed=0 (produces 0)
when you exceed the natural word size of an unsigned int on your
host architecture, an overflow error will be returned.
there are remaining issues to be fixed surrounding PRNG
reproducibility, in that things like til_module_randomize_setup()
doesn't currently accept a seed value. However it doesn't even
use rand_r() currently, but when it invokes desc->random() the
module's random() implementation should be able to use rand_r()
and needs to be fed the seed. So that all still needs wiring up
to propagate the root seed down everywhere it may be relevant.
|
|
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.
|
|
This introduces a very naive unoptimized moire interference
pattern module, it's rather slow complete with a sqrtf() per
pixel per center.
|
|
- 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.
|
|
Just assume a fragment has been logically cleared after
til_module_render() has done all its potential steps.
I'm not certain this doesn't break some existing assumptions WRT
fragmented/threaded clears and their propagation out to the outer
frame.
But I've been operating under the assumption that this was
already happening in terms of an implicit setting of
til_fb_fragment_t.cleared after a module's render happened.
Except I don't see anything in the existing code or history
actually doing that, which is odd.
For modules that don't invoke til_fb_fragment_clear() explicitly
because they are frame-fillers (think submit, swab, ray, julia,
plasma, these are all full-frame renders that don't benefit from
pre-clearing), they weren't leaving fragment->cleared set,
despite having fully initialized the frame's contents.
We should be able to just assume after prepare/render/finish has
happened for a given module, the target fragment has been
cleared.
Commit 4e5286 had introduced somewhat complicated .cleared
maintenance and propagation for threaded renders, but when we
just treat all finished module renders into a given fragment as
logically clearing the fragment we can just skip all that.
|
|
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.
|
|
rtv special-cased handling a nil module to mean clear the
fragment, and called this "none"
But it really makes more sense for rtv to treat "none" as not
doing anything at all for its snow_module - not even blanking.
And it would be nice to have a consistent way to express a blank
module throughout rototiller, so this introduces a concept of
built-in modules accessible only via explicit lookup by name
which don't get enumerated via til_get_modules(), as they're
inherently uninteresting more utility-oriented modules for use by
other modules.
For now it's only "blank" that constitutes the built-ins list,
but expanding this is only a matter of introducing more modules
there. Future commits will rework rtv to use "blank" in place of
its current "none", and rtv's "none" will be reworked to
represent no snow mechanism at all, obviating the need to specify
snow_duration=0,snow_module=none required today.
|