Age | Commit message (Collapse) | Author |
|
While the montage doesn't deeply thread the per-tile/module rendering,
the per-frame rendering is threaded with a work unit granularity of
every module's tile.
Meaning every module renders its tile in a single thread, but the
tiles are all rendered in parallel.
For the most part this works, and will only work better as more modules
are added to rototiller increasing the granularity. In the mean time
it's a bit coarse and some modules can be a lot more costly to render
than others, and there can be a shortage of modules to schedule on idle
CPUs.
It would be an interesting task to try make each module's tile get
subfragmented elastically. I didn't make any attempt to do that, but
it might even be worthwhile on hidpi screens where even those small
tiles may have a whole lot of pixels, especially on manycore CPUs.
|
|
|
|
I'd like the output to fill the range -1..+1, but it's not doing that and I'm
uncertain on what exactly the scaling factor should be here.
In one reference a factor of 1/sqrt(.75) is specified, but in my tests that
doesn't seem to quite fill the range but it doesn't seem to blow it out so
it seems safe for now.
|
|
|
|
This requires a forward declaration of v3f_t and changing din()
to take a v3f_t *.
The swab module needed updating to supply a pointer type and a
v3f_t definition.
This is being done so din.h users can have their own v3f
implementations. I might consolidate all the duplicated vector
code scattered throughout the libs and modules, but for now I'm
carrying on with the original intention of having modules be
largely self-contained. Though the introduction of libs like
ray and din has certainly violated that a bit already.
|
|
Since snow_context_t needs another member anyways, stick n_cpus in there
to inform the fragmenter of precisely how many fragments to make.
This renderer doesn't benefit from tiling or any such locality, so it uses
the slice fragmenter and really only benefits from as many fragments as there
are CPUs. Any additional fragments is just wasted fragmenting overhead.
|
|
Snow was already threaded, but used a global seed with rand_r() meaning
the CPUs were hammering on the same address. There wasn't any locking or
atomics, as it isn't terribly critical when generating white noise if the
seed access is racy. But the writes still caused cache lines to ping-pong.
This commit gives a ~15.5% speedup in my measurements on an i7-2640M.
Note without the padded union, using just an array of ints, zero gain
is realized. I used a padding of 256 just to have some headroom, x86
is 64 but other CPUs vary, POWER9 is 128 for example.
|
|
This repurposes the generic fb_fragment_tile_single() to better fit
the montage use case.
Partially covered areas are simply skipped, and tiles no longer need
to be square.
In determining the tile width and height, I'm just using the sqrt of
the number of modules and dividing the frame width and height. But
when the sqrt has a fraction, it's rounded up on the width divide
and rounded down on the height divide. So the width gets the extra
column of tiles, and the height just throws away the fraction.
I think it's OK for now, until someone gets a bug up their ass and
wants to avoid having vacant tiles in the bottom right corner when
the number of modules doesn't cooperate.
One problem with the just skipping partially covered areas is they
don't get zeroed out - no fragment is ever generated for them.
To fix that there will prolly have to be a fb_fragment_zero() of
the frame @ prepare :(. I guess it wouldn't be the end of the
world if the fragmenter itself zeroed out skipped regions. It's
kind of an ugly layering violation but this is a private montage-
specific fragmenter.
|
|
The old approach was just to get things working, it's preferable
to not have empty tiles on-screen where modules were skipped and
have all tiles be smaller to accomodate vacancies.
Now the modules list gets pruned @ context create, so the skipping
only happens once and everywhere else is looking at a modules list
and count of only the keepers.
I also added stars to the skipped modules, for now, since both stars
and pixbounce malfunction when the fragment size changes.
|
|
Not doing this produces especially visible artifacts when shown
by rtv.
|
|
Segfaults were observed when montage came up in rtv, since pixbounce
doesn't seem to be rendering properly at all just skip it for now.
I suspect what's happening is rtv ran pixbounce before running montage,
and pixbounce caches fragment knowledge @ initialization. So when
montage ran pixbounce in a tile, that stale fragment knowledge was used
and caused scribbling.
Stars probably has similar problems actually.
|
|
This is somewhat unfinished as it uses the generic tiled fragmenter
that's not interested in appearances but prioritizes total coverage
and simplicity.
Montage should have its own tiler that can produce non-square and even
non-uniform tile dimensions, prioritizing filling the screen with
mostly-uniform tiles.
But that's a TODO item, this is good enough for now and exercises some
fragment details previously irrelevant and often ignored/broken in
modules.
The pixbounce module in particular seems completely broken with small
fragment sizes.
|
|
Mechanical change removing abbreviation for consistency
|
|
This introduces a stricter coupling and requirement for modules
supplying a fragmenter in their prepare_frame() to only receive
fragments produced by *their* fragmenter at their render_fragment().
When modules don't explicitly perform any fragmenting they can't
really make much use of this number as it will reflect an arbitrary
fragmenting pass's perspective.
But when modules do perform their own frame fragmenting, they can
assume any fragment supplied to their render function will have been
generated by it. This needs to be enforced a bit in the code.
The current use case is montage using a fragmenter for tiling the
montage in a threaded render. The fragment numbers map to the
modules to be rendered in the tiles. As long as modules can assume
their fragmenter will always be what produces their fragments, this
is perfectly fine.
|
|
The put_pixel helpers really needed reworking to properly handle
subframe fragments modules like montage will utilize. I had the
stride present as it's convenient for a number of modules that
maintain a buf pointer as they progress down a row, but the pitch
is more applicable to put_pixel for scaling the y coordinate.
Now there's both pitch and stride so everyone's happy with what's
most convenient for their needs.
|
|
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.
|
|
This makes the visualization more interesting by adding more variety.
|
|
To facilitate random setting of these flexible string-oriented
settings, support a random helper supplied with the description.
This helper would return a valid random string to be used with the
respective setting being described.
Immediate use case is the rtv module, which also gets fixed up to
use it in this commit.
|
|
This is just a quick stab at randomizing settings, only multiple
choice setings are randomized currently.
For modules with settings, a new Settings: field is added to the
caption showing the settings as the arguments one would pass to
rototiller's module argument.
|
|
This maps a different Z-slice through the noise field to each color
channel. The slices are moved up and down through the field over
time, and the size of the area each color samples is tweaked a bit
to make them less coherent with the noise field cells.
It could be improved, but I think the output is already neat enough
to be worth sharing.
|
|
This is a 3D noise field addressed as a unit cube.
The caller supplies the resolution of the noise field in three
dimensions.
I've just pulled in my v3f.h here, but it probably makes sense to
later on move vector headers into libs/ and share them. Later.
It's called din as in noise, because it's shorter than perlin and
noise.
|
|
|
|
|
|
Consolidate the time() calls in setup_next_module() by using a now
variable.
|
|
This broke when snow was added.
|
|
The idea is to have captions similar to how MTV did back in the 80s.
It'd be nice to make the text resolution independent, but this is a
good first stab for an afternoon of tooling around.
|
|
This is as basic as it gets, the only fanciness is it recognizes
newlines and supports horizontal and vertical justification.
As this is intended to be run from potentially threaded fragmenter
renderers, it receives a fragment and *frame* coordinates for the
text to be rendered. If the text doesn't land in the given fragment,
nothing gets drawn.
Currently this is not optimized at all. There's a stubbed out rect
overlap test function which could be used to avoid entering the
text rendering loop for fragments with zero overlap, that's an obvious
low-hanging fruit optimization. After that, skipping characters
that don't overlap would be another obvious thing.
As-is the text render loop is always entered and the bounds-checked
put pixel helper is used. So every fragment will incur the cost of
rendering the full string, even when it's not visible.
For the rtv captions this isn't a particularly huge deal, but stuff
to improve upon in the future.
|
|
The rtv module needs to show some captions, so I'm adding a minimal
bitmap ascii text renderer.
|
|
Also sort the modules alphabetically.
Now that the major memory leaks are addressed (sparkler), make
the rtv module the default since it gives the user an automated
tour of all the modules. Explicit module use is more aimed at
tinkerers playing with a specific module's code either creating
their own or modifying an existing one, but isn't really desirable
as the default flow.
|
|
This simplifies the bsp code while addressing cleanup
|
|
This assert prevents using the chunker for efficient freeing,
maybe in the future add a flag for toggling this but for now
it can just be commented out.
|
|
particles_free() didn't do all the necessary cleanup.
bsp_free() remains mostly unimplemented. I think this wasn't done at
the time because I was thinking bsp.c should use the chunker, then
cleanup is just a matter of freeing the chunker instead of traversing
the bsp.
|
|
Use setting_desc_check() before storing a value.
|
|
This area needs more work, but this helps a little.
|
|
|
|
Rudimentary setting value checking against the description.
For now it just enforces the multiple-choice stuff, I'm undecided
on the regex support for now. It'd just be nice to throw some
more informative errors when cli arguments are incorrect for things
like fullscreen=yes when it only knows fullscreen=on/off.
|
|
More accurate name, this variable doesn't contain defaults, it
controls the use of defaults.
|
|
|
|
This uses the newly added snow module as a transition between modules
|
|
I wanted to add some noise to the rtv module and figured why not
just add a snow module and make rtv pass through it briefly when
switching modules.
It's not interesting by itself, but as more composite/meta modules
like rtv get made it might be handy beyond rtv.
|
|
This is sort of a meta renderer, as it simply renders other
modules in its prepare_frame() stage. They're still threaded
as the newly public rototiller_module_render() utilizes the
threading machinery, it just needs to be called from the serial
phase @ prepare_frame().
I'm pretty sure this module will leak memory every time it changes
modules, since the existing cleanup paths for the modules hasn't
needed to be thorough in the least. So that's something to fix
in a later commit, go through all the modules and make sure their
destroy_context() entrypoints actually cleans everything up.
See the source for some rtv-specific TODOs.
|
|
Adds:
rototiller_lookup_module()
rototiller_get_modules()
rototiller_module_render()
there should probably be more helpers for dealing with context create
and destroy, but this is enough for some experimentation.
|
|
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.
|
|
Initially I was going to make 32 vs. 64 be a setting, but decided
now that SDL is supported it's fairly likely there will be odd fb
dimensions (arbitrary window sizes). Since this never really brought
anything of significant value, just drop the version that mostly
just demonstrated how to pack multiple pixels into a single u64 write
to the framebuffer more than anything else.
|
|
This removes the submit-softly module, instead using a runtime
setting to toggle bilinear interpolation on the submit module.
|
|
|
|
|
|
|
|
Viscosity and diffusion are supported, it'd be neat to add a
configurable size (the ROOT define) for the flow field in the
future.
I didn't go crazy here, it's just a list of orders of magnitude you
choose from for each. It'd probably be more interesting to change
this into a single knob with descriptive names like "smoke" "goop"
"water" mapping to a LUT.
|