diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/modules/checkers/checkers.c | 40 |
1 files changed, 40 insertions, 0 deletions
diff --git a/src/modules/checkers/checkers.c b/src/modules/checkers/checkers.c index f04ebf0..d6530eb 100644 --- a/src/modules/checkers/checkers.c +++ b/src/modules/checkers/checkers.c @@ -106,6 +106,46 @@ static til_module_context_t * checkers_create_context(const til_module_t *module /* since checkers is already threaded, create an n_cpus=1 context per-cpu */ if (til_module_create_contexts(module, stream, seed, ticks, 1, ctxt->setup->fill_module_setup, n_cpus, ctxt->fill_module_contexts) < 0) return til_module_context_free(&ctxt->til_module_context); + + /* XXX: you might be wondering: why not just use a single context, render into a tile-sized frame buffer in prepare_frame(), + * and just copy from that into the filled cells in threaded render_fragment()? it would avoid all this context clones + * sharing the same path complexity... and if the copy applied alpha/transparency, you'd still get overlay effects. + * What gives? + * + * The answer is: overlay effects go beyond simple transparencies. With fragment snapshotting, arbitrary sampling of + * anywhere within the incoming fragment's contents while writing the fragment is facilitated, so you get use cases like + * drizzle,style=map which treat the "puddle" as a sort of normal map and read in pixel colors from wherever the normal + * aims. And there's sampler overlays like voronoi and checkers, which fill arbitrary regions using the incoming color + * from an arbitrary pixel. For any of these, rendering once into an intermediate single tile-sized frame buffer would + * sample the blank tile-sized frame buffer, so they'd just be blank. It's more like what they'd need is to store the + * relative coordinates to read from per-pixel, instead of the final pixel data, in the tile-sized frame buffer, combined + * with encoding what operation to do on what was read. Instead of that complexity, I'm just cloning the contexts with + * identical seed+settings, and running them per-cpu. It may be interesting to experiment with storing source coordinates + * with color+operation information in a different "sampler" type of frame... but all these modules would need to then + * learn to store that instead of just always producing pixels. It'd be a substantial invasive change, and would constrain + * modules to work within what that sampler frame could express, whereas this context cloning lets the module authors just + * implement whatever they want for producing pixels. + * + * It might seem like a lot of trouble just for a silly checkers module, but ensuring this works properly for checkers + * removes the friction of doing the same kind of fill_module= instancing for concurrency in any future modules. + * + * TODO: a remaining problem to address surrounding context clones is the "ref" builtin, and til_stream_find_module_contexts() + * which it relies on. Imagine checkers uses fill_module=ref,path=/path/to/context/without/clones with things as-is. + * There would be n_cpus ref contexts in checkers, and each of those rendering concurrently will find the same single context + * at /path/to/context/without/clones on the stream. So they'd racily render while sharing a single context, which might + * crash - depending on what module that context belongs to. What needs to happen here is when the "ref" clones lookup + * the context @ path to use, they need a clone number to propagate down to til_stream_find_module_contexts(), which + * would have been assigned when the n_cpus ref clones were created for checkers. Then til_stream_find_module_contexts() + * can ensure the context returned has the same clone number within the found path. But there's still a need to actually + * create the clones when they don't exist. Keep in mind, the /path/to/context/without/clones will have just a + * single context, and we're going to want n_cpus of them from the same path via "ref". So the one that exists needs to be + * cloned on demand, in a race-free manner likely requiring some locking, and it will require the cloned context's module + * to perform the actual cloning in all but the simples of cases. So it looks like til_module_t.clone_context() is needed, + * and should be asserted as present when til_module_t.destroy_context() is present (and vice versa), as the presence of + * either strongly implies the context refers to external resources. And in the absence of a clone_context() method, we + * can have a default "dumb" clone that just duplicates the context with a malloc() and memcpy(). This would also require + * adding a size member to til_module_context_t, which is straightforward to add. + */ } return &ctxt->til_module_context; |