Search Unity

Question branch or keyword? which is fastest in URP? (cost per pixel)

Discussion in 'Shader Graph' started by laurentlavigne, Dec 23, 2020.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,335
    To get multiple features in a shader is there a difference between a branch and a local keyword?
    I understand that SRP breaks with keyword since they produce a variant and not branch so it's not about batching, it's more about the cost per pixel.

    upload_2020-12-23_7-32-29.png
    or
    upload_2020-12-23_7-32-53.png
     

    Attached Files:

  2. Oxeren

    Oxeren

    Joined:
    Aug 14, 2013
    Posts:
    121
    The issue is that shader graph's branch node is not actually a conditional if/else branch, but is just a ternary operator wrapped in a function and it always evaluates both branches (see branch node documentation).
    I also wanted to use branching to create a kind of uber shader that would be SRP-batcher friendly, so I had to use custom functions to implement actual conditional logic. From my limited tests, static branching (i.e. when you condition on material properties, not vertex/fragment data) works very well and does save GPU time by not computing unnecessary stuff. Branches supposedly have their own cost, but as far as I understand it is more or less insignificant.
     
  3. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,335
    is this static branch?
    upload_2020-12-23_14-0-43.png
     
  4. Oxeren

    Oxeren

    Joined:
    Aug 14, 2013
    Posts:
    121
    No, if you use a keyword there will be no branching in the shader, there will be two separate shader variants, and it's gonna break SRP-batching. But if you create a property (not a keyword, just a property), and then use it in a custom function in a condition of a regular if-else block, you're gonna get a shader with static branching. Static branching means that the result of the condition check is known in advance to be the same for all invocations of the shader during a draw call, so only one branch is gonna be evaluated. Dynamic branching, on the other hand, happens when you condition on things like vertex colors or UVs that may be different between invocations, so it may lead to branch divergence, meaning both branches will have to be evaluated. (All of this is a bit of an oversimplified explanation, but the general idea is the same)
     
    laurentlavigne likes this.
  5. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,335
    something like that where predicate is a bool property?
    upload_2020-12-24_9-27-12.png
     
  6. Oxeren

    Oxeren

    Joined:
    Aug 14, 2013
    Posts:
    121
    That's close, the basic idea is to use conditionals like this, but the thing is, in that case both trueValue and falseValue will be calculated before the condition is evaluated, so this setup will basically be the same as lerping between those values or using a ternary operator (the same way Branch node works at the moment). I don't know, maybe some smart shader compilers can optimize stuff like that, but I would not count on that. So to take advantage of branching you'll have to put all logic that is used to calculate trueValue and falseValue inside the if/else block of your custom function. That kinda diminishes the point of using shader graph, since you have to write a lot of code in hlsl anyway, but that's the only way I know of.