Shader Dynamic branch not working?

Some info from chat gpt…

When Dynamic Branching Can Hurt Performance:

  1. Branch Divergence:
  • 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.
  1. 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?

in the material graph → right click → custom

click the node

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

i just tested the dynamic branch node with profile gpu with the following settings…

r.ShowMaterialDrawEvents 1
r.EmitMeshDrawEvents 1
r.Nanite.ShowMeshDrawEvents 1

This displayed the amount of ms … basically with dynamic branching its .19ms without its .15ms…

So the dynamic branching node actually seems to be more expensive according to profile gpu. So does not seem to work…

I do not know how to write an entire shader branch in code so i cant really do that…

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.

Stuff like this guy’s channel also help: https://www.youtube.com/watch?v=bp7REZBV4P4

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!

In practice, this seems to disagree.

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.

Output plus shader


For the guy asking how to write this, this is the custom-node:

Hope this helps.

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.

1 Like

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.