Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We’re making changes to the Unity Runtime Fee pricing policy that we announced on September 12th. Access our latest thread for more information!
    Dismiss Notice
  3. Dismiss Notice

Feature Request URP/HDRP Shader graph code gen optimization

Discussion in 'Graphics Dev Blitz Day 2023 - Q&A' started by optimise, May 25, 2023.

  1. optimise

    optimise

    Joined:
    Jan 22, 2014
    Posts:
    1,965
    Is there any plan to make URP/HDRP Shader graph code gen much more efficient and optimized shader code that can compete with hand written shader code? From what I know currently the performance is still very slow compare with hand written code.
     
  2. jbooth

    jbooth

    Joined:
    Jan 6, 2014
    Posts:
    5,432
    HLSL is a very simple language - most of the code written by a shader graph gets optimized out by the compiler, because it's lots of temp variables, etc. So the size of the output code you see is not necessarily indicative of the performance of that code.

    The slowness between hand written vs. shader graph code is usually not at the compiler level, as much as it's about who's writing it. There's a common misconception that writing in a shader graph produces slower code, but that's not always the case.

    For instance, if you make a shader graph that samples two PBR texture sets and blends them based on a slider, it's likely going to produce code that runs just as fast as hand written code does. But when you get into more complex 'system' level shaders, like terrain shaders, which can sample hundreds of textures, there are more opportunities for optimizations to be easily hidden by a graph.

    For instance, when you sample a normal map in a graph it doesn't write:

    Code (CSharp):
    1. float3 normal = tex2D(_tex, uv);
    it writes:

    Code (CSharp):
    1. float3 normal = UnpackNormal(tex2D(_tex, uv));
    With the unpack normal function doing some math operations to convert the normal to -1 to 1 space and reconstruct the Z component using a sqrt.

    However, if your shader is working with lots of normal maps, it can take a shortcut of blending them together and reconstructing the Z component at the end, saving a bunch of math (with some possible mathematical differences that are visually hard to see IMO)

    You could do this same optimization in a shader graph by understanding the code it's writing and changing the graph - but being at a higher abstraction we tend to forget about the code that it's actually writing. And as a shader graph node encapsulates more code, the problem might grow. But you could write the code for that abstracted node in a more efficient manner for your use case manually instead of using the nice version.

    Finally, there are things just not exposed in a graph - like proper branching, which would be difficult to expose and explain to the average graph user.

    As such, I'm a big fan off node graphs for customizing one off shaders for art assets, but not a big fan of them for large systematic shaders like terrain shaders and mega-shaders. However, the new blocks framework, might provide some hope of being able to combine the best of both worlds here.
     
    Last edited: May 25, 2023
    NotaNaN, shikhrr, dnach and 4 others like this.