Age | Commit message (Collapse) | Author |
|
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.
|
|
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.
|
|
- 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.
|
|
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.
|
|
Just one case, modules/submit, was using 32x32 tiles and is now
using 64x64. I don't expect it to make any difference.
While here I fixed up the num_cpus/n_cpus naming inconsistencies,
normalizing on n_cpus.
|
|
Fragmenting is often dimensioned according to the number of cpus,
and by not supplying this to the fragmenter it was made rather
common for module contexts to plumb this themselves - in some
cases incorporating a context type/create/destroy rigamarole
for the n_cpus circuit alone.
So just plumb it in libtil, and the prepare_frame functions can
choose to ignore it if they have something more desirable onhand.
Future commits will remove a bunch of n_cpus from module contexts
in favor of this.
|
|
This brings something resembling an actual type to the private
objects returrned in *res_setup. Internally libtil/rototiller
wants this to be a til_setup_t, and it's up to the private users
of what's returned in *res_setup to embed this appropriately and
either use container_of() or casting when simply embedded at the
start to go between til_setup_t and their private containing
struct.
Everywhere *res_setup was previously allocated using calloc() is
now using til_setup_new() with a free_func, which til_setup_new()
will initialize appropriately. There's still some remaining work
to do with the supplied free_func in some modules, where free()
isn't quite appropriate.
Setup freeing isn't actually being performed yet, but this sets
the foundation for that to happen in a subsequent commit that
cleans up the setup leaks.
Many modules use a static default setup for when no setup has
been provided. In those cases, the free_func would be NULL,
which til_setup_new() refuses to do. When setup freeing actually
starts happening, it'll simply skip freeing when
til_setup_t.free_func is NULL.
|
|
This is a preparatory commit for cleaning up the existing sloppy
global-ish application of settings during the iterative _setup()
call sequences.
Due to how this has evolved from a very rudimentary thing
enjoying many assumptions about there ever only being a single
module instance being configured by the settings, there's a lot
of weirdness and inconsistency surrounding module setup WRT
changes being applied instantaneously to /all/ existing and
future context's renderings of a given module vs. requiring a new
context be created to realize changes.
This commit doesn't actually change any of that, but puts the
plumbing in place for the setup methods to allocate and
initialize a private struct encapsulating the parsed and
validated setup once the settings are complete. This opaque
setup pointer will then be provided to the associated
create_context() method as the setup pointer. Then the created
context can configure itself using the provided setup when
non-NULL, or simply use defaults when NULL.
A future commit will update the setup methods to allocate and
populate their respective setup structs, adding the structs as
needed, as well as updating their create_context() methods to
utilize those setups.
One consequence of these changes when fully realized will be that
every setting change will require a new context be created from
the changed settings for the change to be realized.
For settings appropriately manipulated at runtime the concept of
knobs was introduced but never finished. That will have to be
finished in the future to enable more immediate/interactive
changing of settings-like values appropriate for interactive
manipulation
|
|
Originally the thinking was that rototiller modules would become
dlopen()ed shared objects, and that it would make sense to let
them be licensed differently.
At this time only some modules I have written were gplv3, Phil's
modules are all gplv2, and I'm not inclined to pivot towards a
dlopen model.
So this commit drops the license field from til_module_t,
relicenses my v3 code to v2, and adds a gplv2 LICENSE file to the
source root dir. As of now rototiller+libtil and all its modules
are simply gplv2, and anything linking in libtil must use a gplv2
compatible license - the expectation is that you just use gplv2.
|
|
Largely mechanical rename of librototiller -> libtil, but
introducing a til_ prefix to all librototiller (now libtil)
functions and types where a rototiller prefix was absent.
This is just a step towards a more libized librototiller, and til
is just a nicer to type/read prefix than rototiller_.
|
|
This is a first approximation of separating the core modules and
threaded rendering from the cli-centric rototiller program and
its sdl+drm video backends.
Unfortunately this seemed to require switching over to libtool
archives (.la) to permit consolidating the per-lib and
per-module .a files into the librototiller.a and linking just
with librototiller.a to depend on the aggregate of
libs+modules+librototiller-glue in a simple fashion.
If an alternative to .la comes up I will switch over to it,
using libtool really slows down the build process.
Those are implementation/build system details though. What's
important in these changes is establishing something resembling a
librototiller API boundary, enabling creating alternative
frontends which vendor this tree as a submodule and link just to
librototiller.{la,a} for all the modules+threaded rendering of
them, while providing their own fb_ops_t for outputting into, and
their own settings applicators for driving the modules setup.
|
|
Most modules find themselves wanting some kind of "t" value increasing
with time or frames rendered. It's common for them to create and
maintain this variable locally, incrementing it with every frame
rendered.
It may be interesting to introduce a global notion of ticks since
rototiller started, and have all modules derive their "t" value from
this instead of having their own private versions of it.
In future modules and general innovations it seems likely that playing
with time, like jumping it forwards and backwards to achieve some
visual effects, will be desirable. This isn't applicable to all
modules, but for many their entire visible state is derived from their
"t" value, making them entirely reversible.
This commit doesn't change any modules functionally, it only adds the
plumbing to pull a ticks value down to the modules from the core.
A ticks offset has also been introduced in preparation for supporting
dynamic shifting of the ticks value, though no API is added for doing
so yet.
It also seems likely an API will be needed for disabling the
time-based ticks advancement, with functions for explicitly setting
its value. If modules are created for incorporating external
sequencers and music coordination, they will almost certainly need to
manage the ticks value explicitly. When a sequencer jumps
forwards/backwards in the creative process, the module glue
responsible will need to keep ticks synchronized with the
sequencer/editor tool.
Before any of this can happen, we need ticks as a first-class core
thing shared by all modules.
Future commits will have to modify existing modules to use the ticks
appropriately, replacing their bespoke variants.
|
|
The existing code conflated the rendered frame dimensions with
what's essentially the virtual camera's film dimensions. That
resulted in a viewing frustum depending on the rendered frame
dimensions. Smaller frames (like in the montage module) would
show a smaller viewport into the same scene.
Now the view into the scene always shows the same viewport in
terms of the frustum dimensions for a given combination of
focal_length and film_{width,height}.
The rendered frame is essentially a sampling of the 2D plane
(the virtual film) intersecting the frustum.
Nothing is done to try enforce a specific aspect ratio or any
such magic. The caller is expected to manage this for now, or
just ignore it and let the output be stretched when the aspect
ratio of the output doesn't match the virtual film's aspect
ratio.
In the future it might be interesting to support letter boxing or
such things for preserving the film's aspect ratio.
For now the ray module just lets things be stretched, with
hard-coded film dimensions of something approximately consistent
with the past viewport.
The ray module could make some effort to fit the hard-coded film
dimensions to the runtime aspect ratio for the frame to be
rendered, tweaking things as needed but generally preserving the
general hard-coded dimensions. Allowing the frustum to be
minimally adjusted to fit the circumstances... that might also be
worth shoving into libray. Something of a automatic fitting
mode for the camera.
|
|
Mechanical change removing abbreviation for consistency
|
|
Mostly mechanical change, though threads.c needed some jiggering to
make the logical cpu id available to the worker threads.
Now render_fragment() can easily addresss per-cpu data created by
create_context().
|
|
Back in the day, there was no {create,destroy}_context(), so passing
num_cpus to just prepare_frame made sense. Modules then would
implicitly initialize themselves on the first prepare_frame() call
using a static initialized variable.
Since then things have been decomposed a bit for more sophisticated
(and cleaner) modules. It can be necessary to allocate per-cpu data
structures and the natural place to do that is @ create_context(). So
this commit wires that up.
A later commit will probably have to plumb a "current cpu" identifier
into the render_fragment() function. Because a per-cpu data structure
isn't particularly useful if you can't easily address it from within
your execution context.
|
|
color banding has been quite visible, and somewhat expected with a
direct conversion from the linear float color space to the 8-bit
integral rgb color components.
A simple lookup table is used here to non-linearly map the values, table
generation is taken from Greg Ward's REAL PIXELS gem in Graphics Gems II.
|
|
Rather than require adding -Isrc/libs/$lib to every Makefile.am for
every lib used, just add -Ilibs to those makefiles and prefix the lib
dir in the #include <> header paths.
Later I'll probably just move the -Isrc/libs someplace common so the
per-module Makefile.am doesn't need to bother with this stuff.
|
|
This is the first step of breaking out all the core rendering stuffs
into reusable libraries and making modules purely compositional,
consumers of various included rendering/effects libraries.
Expect multiple modules leveraging libray for a variety of scenes and
such. Also expect compositions mixing the various libraries for more
interesting visuals.
|
|
|
|
Fixes silly cosmetic error in configure output for checking libdrm...
|
|
also const the ray_euler_t basis
|
|
|
|
This moves the per-object _prepared state into ray_render_object_$type
structs with all the rendering-related object methods switched to
operate on the new render structs.
Since the current rendering code just makes all these assumptions
about light objects being point lights, I've just dropped all the
stuff associated with rendering light objects for now. I think it
will be refactored a bit later on when the rendering code stops
hard-coding the point light stuff.
These changes open up the possibility of constifying the scene and
constituent objects, now that rendering doesn't shove the prepared
state into the embedded _prepared object substructs.
|
|
This introduces ray_render_t, and ray_render.[ch].
The _prepared member of ray_scene_t has been moved to ray_render_t,
and the other _prepared members (e.g. objects) will follow.
Up until now I've just been sticking the precomputed state under
_prepared members of their associated structures, and simply using
convention to enforce anything resembling an api boundary. It's
been convenient without being inefficient, but I'd like to move
the ray code into more of a reusable library and this wart needs
to be addressed.
The render state is also where any spatial indexes will be built
and maintained, another thing I've been experimenting with.
Note most of the churn here is just renaming ray_scene.c to
ray_render.c. A nearly global s/ray_scene/ray_render/ has occurred,
now that ray_scene_t really only serves as glue to bind objects,
lights, and scene-global properties into a cohesive unit.
|
|
Before I can clean up the ray_scene_t._prepared kludge I need a
place to keep state from frame prepare to render, enter context.
Future commits will migrate the _prepared stuff into a separate
ray_render_t which is constructed on prepare then acted on in
fragment render.
Then spatial acceleration structures may be added, constructed
at prepare phase and shared across the concurrent rendering.
|
|
Remove some extraneous indentation
|
|
Commit 445e94 switched to using sentinel objects, but missed removal
of these obsoleted object counts.
|
|
There's no point computing more reflections if they're not going
to contribute substantially to the resulting sample. Previously
the max depth threshold solely controlled how many times a given
ray could reflect, this commit introduces a minimum relevance as
well. Value may require tuning, may actually make sense to move
into the scene description as a parameter.
Brings a minor frame rate improvement.
|
|
Just cast buf to (void *) for the pointer arithmetic, stride is in
units of bytes and no assumptions should be made about its value
such as divisability by 4.
|
|
|
|
Rather than laying out all fragments in a frame up-front in
ray_module_t.prepare_frame(), return a fragment generator
(rototiller_fragmenter_t) which produces the numbered fragment
as needed.
This removes complexity from the serially-executed
prepare_frame() and allows the individual fragments to be
computed in parallel by the different threads. It also
eliminates the need for a fragments array in the
rototiller_frame_t, indeed rototiller_frame_t is eliminated
altogether.
|
|
Trivial optimization eliminates some instructions from the hot path,
no need to maintain a separate index from the current object pointer.
|
|
Previously every fb_fragment_t (and thus thread) was constructing
its own ray_camera_frame_t view into the scene, duplicating some
work.
Instead introduce ray_camera_fragment_t to encapsulate the truly
per-fragment state and make ray_scene_render_fragment() operate
on just this stuff with a reference to a shared
ray_camera_frame_t prepared once per-frame.
Some minor ray_camera.c cleanups sneak in as well (prefer multiply
instead of divide, whitespace cleanups...)
|
|
Currently fragments always start at the left edge of the frame, but
when switching to a tiling fragmenter this is no longer true and
causes visible errors.
|
|
ray:object intersection coordinates were incorrectly being computed
relative to the ray origin using a subtraction instead of addition, a
silly mistake with surprisingly acceptable results. Those results
were a result of other minor complementary mistakes compensating to
produce reasonable looking results.
In the course of experimenting with an acceleration data structure it
became very apparent that 3d space traversal vectors were not behaving
as intended, leading to review and correction of this code.
|
|
For now, a simple cpu multiplier of 64 is used.
fb_fragment_t needs a tiling fragment divider added...
|
|
Small speedup, I personally find the code cleaner this way too.
Everything in the hot path should now be inlined, no function calls.
|
|
We can just assume the object which reflected the ray being tracing
isn't going to be intersected. Maybe later this assumption no longer
holds true, but it is true for now.
|
|
This gets rid of some computation on the primary ray:plane intersection tests
The branches on depth suck though... I'm leaning towards specialized primary
ray intersection test functions.
|
|
This gets rid of some computation on the primary ray:plane intersection tests
|
|
To enable prepare to precompute aspects of primary rays which all have a
common origin at the camera, bring the camera to ray_object*_prepare() and
bring the depth to ray_object*_intersects_ray() for primary ray detection.
This is only scaffolding, functionally unchanged.
|
|
This may need to be undone in the future when more sophisticated lights,
like area lights, are implemented. For now I can avoid polluting the
objects list with the lights by strictly separating them.
|
|
Remove unnecessary nearest_object check, the distance comparison alone
is sufficient when initialized to INFINITY.
|
|
Just tidying up shade_ray() before more optimizations.
|
|
Trivially removes a ray_3f_mult_scalar() from the hot path.
|
|
We can avoid some unnecessary work at the max depth by checking it in
shade_ray() instead.
|