TSL: How can I ensure a TSL/node material's shader only gets compiled once?

I have a suspicion that TSL materials I create are being re-compiled for each mesh they are used on, regardless if the meshes share the same geometry and material. Is this desired behavior? I thought a material’s shader only gets compiled once per geometry it’s used on? This would mean that 100 meshes using the same geometry and material should only result in 1 compilation of the shader, correct?

1 Like

Just to back you up, I also have a similar observation. I have a TSL function attached to a node of a node material and it looks like it gets compiled for each of the meshes. I’m not sure what exactly happens, but when I reuse the same function several times, I do observe significant delay at start up (measured in seconds).

Possibly relevant: TSL: Slow compilation time · Issue #31674 · mrdoob/three.js · GitHub

1 Like

When you say “TSL function attached to a node of a node material”, do you mean something like this? Because this is also something I found peculiar:

material.positionNode = tsl.Fn(({object: mesh}) => {
    const node = tsl.vec3().toVar();
    // do stuff with the mesh passed to it
    node.y.assign(mesh.position.y);
    return node;
})();

I suspect tsl.Fn() is intended to be compiled per mesh, because the factory function gets passed a Mesh object. The fact that the function gets passed a Mesh object means that it must get recompiled for each one used on, since there is a possibility that you might be using data from the Mesh for something you’re doing in TSL. There is no way for tsl.Fn() to know whether you are using mesh-specific properties in your shader or not, so it just assumes you are to be safe.

So, I was just wondering if it were possible to properly construct a TSL shader so that it does not follow this recompilation behavior. Perhaps by avoiding calls to tsl.Fn(), or maybe allowing us to pass a true or false flag to inform tsl.Fn() whether it is mesh-specific or not?

Your have no layout of the function. A function without layout is inlined (i.e. it is not compiled once, and then called multiple times, but its body in inserted wherever it is called, so it is compiled multiple times). For functions like this I try to isolate layout-able parts, but still this does not solve my main issue - the delay. I don’t recall the exact numbers, but they were like this: using the function in one material cases delay 0.2 seconds, using it in 5 materials causes delay 10 seconds.

Apologies if this is unrelated and a dumb question. But can TSL be compiled ahead of time?

It depend the “step” of the TSL pipeline.
From my own testing, the transpiling is done on JS code reading (instant)
But the compiling of the final WGSL only at the first draw of the node material.

The current method to hack this automation (three waiting for a material to be used) Is demonstrated in the “tsl compute attractors particles” example by calling a single renderer.computeAsync( initCompute )before the main rendering loop.

But can we call it ahead of time? Maybe, maybe not :sweat_smile:

1 Like