GPUs execute shaders in groups of pixels (called warps or wavefronts). If pixels in the same group take different branches, the GPU must evaluate both branches, leading to reduced efficiency. This is known as branch divergence.
Overhead of Branching:
Adding branches introduces some overhead due to the additional instructions and potential pipeline stalls. If the branch conditions are not effective in skipping significant work, this overhead might outweigh the benefits.
=========
Coherent Branches:
Ensure that the branches are likely to be coherent within a small area of the screen. For example, using dynamic branching based on screen-space coordinates can lead to divergence, but using it based on material properties (like roughness or metallic) can be more coherent.
Since custom expression supports multiple outputs, most of the whine regarding lack of flow control in that thread becomes obsolete. Put what you need to branch in custom node.
dont really know how to write code for that. Can do bare minimum. Are you saying if i input 2 seperate branches of nodes into the custom node with 2 inputs ⌠thatn i can output whichever i decide by a simple say third input like a bool� And it would work to save performance?
on the left panel, add yer inputs, outputs, and stuff your custom-code in the big-box
further up the thread is an example of just this + a couple videos, one of them is a how-to
I seem to be incorrect, see below. tl;dr put ALL your custom-code, with the outer-branch in the custom-node, since your alpha is always-run to determine what path to take it will be run in the main-graph and passed as an input to the custom-node. the actual switching of one path or the other is inside the custom-node so that is where your either-this or that code goes, what actually executes based on the-test
The way I taught myself the little I know is to make something basic like UVs, texture-sampler, do a multiply for tint. Compile and extract the HLSL from unreal to see how itâs compiled down. Add things and see how the compiled code changes.
Look for examples of custom-code on the forums. It can help to see multiple examples too.
IF you can code in general, itâs not hard, but like any language, learning the specific-syntax, etc.
Iâm NOT an expert, but good tech-support skills, so I can take something functional apart, see where itâs a loop, a this, or a that, and then reassemble as you might need. GL!
Itâs Bezier-grass so the mesh is shaped in the shader. As well I have meshes painting into an RVT roughness channel to act as a grass-mask. Pics below but we can see the stuff getting masked out by the red thing, are not being formed as they should be if wind, etc are being applied. We can see they are still just the raw square-thing I used as the base mesh.
My two WPO paths are distinct from one another and it seems only one is executing, whilst both are outside the custom-node.
Forgive me but Iâm not sure I understand what this is supposed to be demonstrating. I expect every method in this thread to execute correctly and produce the same output, the question is do any of the methods actually using dynamic branching to produce that output?
My understanding is there was some confusion around where to put code being branched.
Unless I am mistaken, and I often am, the two paths tehre are mutually exclusive, as borne out in the pic. The flat-unshaped mesh is -200 units under the landscape, so itâs path is correctly executing. The path that shapes the mesh and does the wind-etc is also correctly executing b/c we can see the meshes above ground.
My point was it seems, at least in this case, that the stuff you want to switch between doesnât seem to have to be in the custom-node. The custom-code is just the switch.
Again, and itâs hard to emote across text, I could be wrong, I Really believe that! So if I am, or I erred, please tell me, but my test?
The question is about whether a âdynamic branchâ is happening in the various contexts in which you can construct branching logic in the material editor.
A dynamic branch meaning, a boolean/expression is used to determine whether to do A or B. It only does one.
Most kinds of branching that you can create in the material editor however donât do that, they evaluate both A and B, then check the boolean/expression and throws away one of them.
For a branch to be an optimization, like the OP wanted, it would need to be a dynamic branch. But as the discourse here has shown⌠it can be difficult to figure out if/when the material editor is actually creating a real dynamic branch. (dynamic branching isnât always a performance optimization, sometimes its faster to evaluate both A+B, but thatâs an entirely different topic)
If your only concern is that the output looks like it is supposed to then yes, having an HLSL block with nothing but a branch in it will work fine. As will all the other options, using a ternary, a step function, boolean math, an if node, an if node wrapped in a material function named âdynamic branchâ, even a lerp will work. Theyâll all get there, but not all of them will actually use a dynamic branch to do it.
It would be great to hear from Epic as to why dynamic branching is not properly implemented in material graphs. Its been like this for so long, and I donât understand why.
My mistake, you are correct. I was searching for it and didnât realize it came after the future-translator. Good to know itâs on the horizon though!
I can see big wins in Substrate for avoiding the cost of a layer/slab of material if the Alpha is 1 or 0.
Substrate already does this for horizontal blends, you only pay the cost for evaluating both BSDFs at the transition region where the values are between 0-1
And I think it also does this with coverage weighting for vertical layers but Iâm not 100% sure.
Yeah, Iâll be thrilled when/if they can eventually support loops. Thatâll eliminate basically all of my remaining custom node usage needs. If they canât do that, hopefully they could at least make calling functions inside of a custom node more intuitive. You have to do some janky stuff using multiple custom nodes just to declare a function so you can loop it.
One important use case for dynamic branching outside of loops is material scalability. Lets say you want the player to have the ability to disable or enable POM on a material via a settings menu. Without a dynamic branch, you must have a separate permutation (for example by using the engineâs built in quality switch) for each material that uses POM - both with and without POM. Or you could set the parameters to 0 but youâd still be paying all the instruction costs, even if you avoid all the POM samples.
But with dynamic branching you can have just one shader permutation that branches to desired version as needed without needing to compile two permutations. It might be slightly more instructions in both cases but that could easily be worthwhile.
Indeed, I can see this cutting down on shader-permutation overhead? If you can have proper-branching, would it not invite the removal of switches in favor of incorporating that branching-behavior. Less complexity in that arena is always a gain.
Ultimately, my takeaway is that we will always be able to help-ensure the execution path is always most efficient. I am with you in loading up a singluar, more-monolithic shader and then letting the most efficient path for that pixel run. I could even see a/the standard of a thing trending towards a âsolidâ shader, a âliquidâ shader, âgasâ shader, etc vs just having plant, rock, person, etcâŚ
The issue is dynamic branching isnât always going to be a performance win which is why I mentioned loops being the most important use case for them. For scalability POMâs iteration/step count can be set outside of the loop which doesnât require dynamic branching. But when youâre inside of the loop you need a dynamic branch to return early, thereâs no way around that.
This makes me wonder because I donât actually know if a dynamic branch would be faster than just setting the iterations/steps to 0 on lower scalability. Neither would require a new shader permutation, and both would skip the loop. Would gating it behind a dynamic branch be faster? No ideaâŚ
Thereâs also already a scalability switch node but I havenât looked into how it actually works behind the scenes; Is it already using dynamic branching or is it just a ternary or static switch in a trenchcoat? I donât knowâŚ
The major cases where I think this is important, thereâs already workarounds for. Setting POM steps as a param outside of the custom node is one example but another is volumetric cloud materials, they are crazy expensive without dynamic branching (especially with raymarched shadows), and to address this Epic added a conservative density input so you can skip the expensive raymarching loop for empty space.
Basically there is probably value here, but I suspect itâs not going to be as big as people are hoping.
Quality switch is just a static switch linked to the built in scalability presets.
I think the rule of thumb I learned was that if a branch saved roughly 12 instructions it was better to branch. But that might be outdated. Even without a loop POM is pretty instruction heavy.
But youâre right that step count and many other settings donât need branching. In my projects I have a custom class of the Game User Settings that stores such things as a user config the material to read.