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. Dismiss Notice

Question Shader.SetTexture, which will perform better? Optimization ideas?

Discussion in 'Shaders' started by marvpaul, Oct 8, 2022.

  1. marvpaul

    marvpaul

    Joined:
    May 9, 2016
    Posts:
    26
    I have a shader with multiple passes, where every pass requires some textures and variables which I need to bind from a C# script.

    I want to execute these passes using Unity and render the result into a RenderTexture.

    As I have many shader passes I came across performance problems while executing the shader on mobile devices using Metal as graphic backend (I used the Profiler and found out my application mainly stucks on Gfx.WaitForPresent, while doing so, my render thread still spends time in Camera.Render, so when I understood this [https://forum.unity.com/threads/pro...nd-editorloop-too-high-with-vsync-off.620854/] correctly, my application is CPU bound, potentially because I use a lot of shader.SetTexture and shader.SetFloat calls) . Therefore I have a question regarding performance:

    Let's assume a simple scenario where I have 4 RenderTextures a, b, c, d and two passes. In the first pass I want to blit from a and b to c and in the second pass I want to blit from c to d.
    Which version is better performance-wise (bind every RenderTexture once to the shader using a separate sampler or bind only the needed RenderTextures on every pass)?
    Also do you have any other ideas how I can potentially increase performance given the sample code (it's highly simplified, my actual shader is more complex for sure)? Is it worth to change from Graphic.Blit to CommandBuffers? Anything else which I can try?

    Version A (bind every RenderTexture ONCE using SetTexture in Start method):
    C#
    Code (CSharp):
    1. Start(){
    2.  
    3. RenderTexture a = new RenderTexture(...);
    4. RenderTexture b = new RenderTexture(...);
    5. RenderTexture c = new RenderTexture(...);
    6. RenderTexture d = new RenderTexture(...);
    7. Material shader = Shader.Find("Unlit/CustomShader");
    8. shader.SetTexture("a", a);
    9. shader.SetTexture("b", b);
    10. shader.SetTexture("c", c);
    11. }
    12.  
    13. Update(){
    14. // Execute first pass
    15. Graphics.Blit(null, c, shader, 0);
    16. // Execute second passs
    17. Graphics.Blit(null, d, shader, 1);
    18. }
    Shader:
    Code (CSharp):
    1. Shader "Unlit/CustomShader"
    2. {
    3. SubShader
    4. {
    5. CGINCLUDE
    6. #include "UnityCG.cginc"
    7. uniform sampler2D a, b, c, d;
    8.  
    9. struct appdata
    10. {
    11. float4 vertex : POSITION;
    12. float2 uv : TEXCOORD0;
    13. };
    14.  
    15. struct v2f
    16. {
    17. float4 vertex : SV_POSITION;
    18. float2 uv : TEXCOORD0;
    19. };
    20.  
    21. v2f vert (appdata v)
    22. {
    23. v2f o;
    24. o.vertex = UnityObjectToClipPos(v.vertex);
    25. o.uv = v.uv;
    26. return o;
    27. }
    28.  
    29. ENDCG
    30. Pass {
    31. NAME "PASSONE"
    32. Blend One OneMinusSrcAlpha
    33. CGPROGRAM
    34. #pragma vertex vert
    35. #pragma fragment frag
    36.  
    37. half4 frag (v2f i) : SV_Target
    38. {
    39. return 0.5 * (tex2D(a, i.uv) + tex2D(b, i.uv));
    40. }
    41. ENDCG
    42. }
    43. Pass {
    44. NAME "PASSTWO"
    45. Blend One OneMinusSrcAlpha
    46. CGPROGRAM
    47. #pragma vertex vert
    48. #pragma fragment frag
    49.  
    50. half4 frag (v2f i) : SV_Target
    51. {
    52. return tex2D(c, i.uv);
    53. }
    54. ENDCG
    55. }
    56. }
    57. }

    Version B (bind only needed RenderTextures every Update using SetTexture, we now save one sampler2D but need to execute SetTexture way more often):
    C#
    Code (CSharp):
    1. Start(){
    2.  
    3. RenderTexture a = new RenderTexture(...);
    4. RenderTexture b = new RenderTexture(...);
    5. RenderTexture c = new RenderTexture(...);
    6. RenderTexture d = new RenderTexture(...);
    7. Material shader = Shader.Find("Unlit/CustomShader");
    8. }
    9.  
    10. Update(){
    11. // Bind
    12. shader.SetTexture("tex1", a);
    13. shader.SetTexture("tex2", b);
    14. // Execute first pass
    15. Graphics.Blit(null, c, shader, 0);
    16.  
    17. // Bind
    18. shader.SetTexture("tex1", c);
    19. // Execute second passs
    20. Graphics.Blit(null, d, shader, 1);
    21. }
    Shader:

    Code (CSharp):
    1. Shader "Unlit/CustomShader"
    2. {
    3. SubShader
    4. {
    5. CGINCLUDE
    6. #include "UnityCG.cginc"
    7. uniform sampler2D tex1, tex2;
    8.  
    9. struct appdata
    10. {
    11. float4 vertex : POSITION;
    12. float2 uv : TEXCOORD0;
    13. };
    14.  
    15. struct v2f
    16. {
    17. float4 vertex : SV_POSITION;
    18. float2 uv : TEXCOORD0;
    19. };
    20.  
    21. v2f vert (appdata v)
    22. {
    23. v2f o;
    24. o.vertex = UnityObjectToClipPos(v.vertex);
    25. o.uv = v.uv;
    26. return o;
    27. }
    28.  
    29. ENDCG
    30. Pass {
    31. NAME "PASSONE"
    32. Blend One OneMinusSrcAlpha
    33. CGPROGRAM
    34. #pragma vertex vert
    35. #pragma fragment frag
    36.  
    37. half4 frag (v2f i) : SV_Target
    38. {
    39. return 0.5 * (tex2D(tex1, i.uv) + tex2D(tex2, i.uv));
    40. }
    41. ENDCG
    42. }
    43. Pass {
    44. NAME "PASSTWO"
    45. Blend One OneMinusSrcAlpha
    46. CGPROGRAM
    47. #pragma vertex vert
    48. #pragma fragment frag
    49.  
    50. half4 frag (v2f i) : SV_Target
    51. {
    52. return tex2D(tex1, i.uv);
    53. }
    54. ENDCG
    55. }
    56. }
    57. }
     
    Last edited: Oct 8, 2022
  2. trickster721

    trickster721

    Joined:
    Jul 21, 2017
    Posts:
    4
    Does version B create new material instances in Update?

    Honestly, what you're describing seems like it might just be too demanding for mobile. How many screens worth of pixels are you trying to draw each frame?
     
  3. marvpaul

    marvpaul

    Joined:
    May 9, 2016
    Posts:
    26
    @trickster721 Thanks for the reply!
    No I don't think it will create a new material instance, I only create an instance once while initialize (Shader.Find("Unlit/CustomShader") as far as I know.

    It's working on mobile with 60FPS but my smartphones get's warm when I use full HD (1920x1080), so I'm interested in optimizing the performance.
     
    Last edited: Oct 9, 2022