Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

OnRenderImage() is SLOW when MSAA is on

Discussion in 'Image Effects' started by colin299, Aug 21, 2016.

  1. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    In my test on android (galaxy note2, GLES2.0),
    OnRenderImage() always slower than OnPreRender+OnPostRender method

    WhyOnRenderImageIsSlow.png

    Question: is OnRenderImage() suppose to be slower? because everyone is using OnRenderImage().

    The only pro of OnRenderImage() is that it can chain image effects together, but since I am targeting mobile,I don't need to chain any image effect, I will just combine them together.


    -------------
    Edit: solution and performance record are in replies below.
     
    Last edited: Aug 23, 2016
    UnityLighting likes this.
  2. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    Conclusion:
    If you need to use any image effect using OnRenderImage(),
    only the following 2 cases can provide good performance (at least I can see really big difference on GLES2 & GLES3 devices).

    -MSAA off, clear flag = solid color
    -MSAA off, clear flag = skybox

    anything other than the 2 cases above, will trigger "Grab Texture", which is GPU->CPU ReadPixel().
    It is SUPER slow on all mobile device I have.(Mali400MP, Adreno(TM)305)
    -------------------------------------------------
    So if you want to do image effect while MSAA on / Clear depth only / Don't clear is needed.
    Please use OnPreRender+OnPostRender method to avoid any Grab Texture,Grab Texture is SUPER slow GPU->CPU glReadPixel().

    The following image show some numbers found in FrameDebugger.
    Test scene just render a sphere, nothing else.
    WhenOnRenderImageIsSlow.jpg
     
    Last edited: Aug 23, 2016
    Ruslank100, UnityLighting and mgear like this.
  3. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    Could someone from unity confirm this. I also tested and get same results.
     
  4. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    Anyone interested to test the difference betweeen OnRenderImage() & OnPreRender+OnPostRender(), here is a package to test.

    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class OnPrePostRender : MonoBehaviour
    5. {
    6.     public Shader ImageEffectShader;
    7.     Material _mat;
    8.     Material Mat
    9.     {
    10.         get
    11.         {
    12.             if (_mat == null)
    13.                 _mat = new Material(ImageEffectShader) { hideFlags = HideFlags.DontSave };
    14.             return _mat;
    15.         }
    16.     }
    17.     public enum MSAA
    18.     {
    19.         _1 = 1,
    20.         _2 = 2,
    21.         _4 = 4,
    22.         _8 = 8
    23.     }
    24.     public MSAA _MSAA;
    25.     RenderTexture mainRT;
    26.     void Start()
    27.     {
    28.  
    29.     }
    30.     void OnPreRender()
    31.     {
    32.         mainRT = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default, (int)_MSAA);
    33.         GetComponent<Camera>().targetTexture = mainRT;
    34.     }
    35.  
    36.     void OnPostRender()
    37.     {
    38.         GetComponent<Camera>().targetTexture = null;
    39.         Graphics.Blit(mainRT, null, Mat);
    40.         RenderTexture.ReleaseTemporary(mainRT);
    41.     }
    42. }
    43.  
    Code (CSharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class OnRenderImageTest : MonoBehaviour
    5. {
    6.     public Shader ImageEffectShader;
    7.     Material _mat;
    8.     Material Mat
    9.     {
    10.         get
    11.         {
    12.             if (_mat == null)
    13.                 _mat = new Material(ImageEffectShader) { hideFlags = HideFlags.DontSave };
    14.             return _mat;
    15.         }
    16.     }
    17.  
    18.     private void OnRenderImage(RenderTexture src,RenderTexture dest)
    19.     {
    20.         Graphics.Blit(src, dest, Mat);
    21.     }
    22. }
    23.  

    Some numbers to prove the difference (MSAA on+OnRenderImage() = slow):

    case1: Adreno(TM)305, MSAAx4, SolidColor clear
    OnPreRender+OnPostRender() - 60fps(16.6ms)
    OnRenderImage() - 27fps(36.3ms)

    case2: Adreno(TM)305, MSAAx4, depth clear
    OnPreRender+OnPostRender() - 60fps(16.6ms)
    OnRenderImage() - 27fps(36.3ms)

    case3: Adreno(TM)305, MSAAx4, skybox clear
    OnPreRender+OnPostRender() - 38fps(26.6ms)
    OnRenderImage() - 24fps(41.3ms)

    "Camera.AAResolve->Grab RenderTexture" was found in FrameDebugger in all 3 case when using OnRenderImage(), but not in OnPreRender+OnPostRender().
    ----------------------
    only the following 2 case are having idenical performance(also same actions by FrameDebugger):
    MSAA off + "clear flag = solid color"
    MSAA off + "clear flag = skybox"
    any other cases, OnRenderImage() will be slower than OnPreRender+OnPostRender()

    You need multi-thread rendering on in order to use FrameDebugger on mobile.

    I will avoid OnRenderImage() from now, because I need MSAA in a mobile project.
     

    Attached Files:

    Last edited: Aug 23, 2016
  5. Tim-C

    Tim-C

    Unity Technologies

    Joined:
    Feb 6, 2010
    Posts:
    2,221
    You can manually allocate an MSAA RT to the camera for rendering to avoid the grab pass. we are working on making this better at the moment.
     
    Ruslank100, Deleted User and colin299 like this.
  6. Rusfighter

    Rusfighter

    Joined:
    Jan 18, 2014
    Posts:
    60
    So is this fixed?
     
  7. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    OnPreRender_OnPostRenderWithMSAA.jpg OnRenderImageWithMSAA.jpg
    tested the above package again in Unity5.6, seems using "OnRenderImage()" & "OnPreRender()+OnPostRender" is still different. "OnRenderImage()" still triggers GrabTexture, which is slow.
     
    Last edited: Apr 9, 2017
  8. Reguluz

    Reguluz

    Joined:
    Nov 9, 2018
    Posts:
    5
    I wrote my post effect on OnPostRender but didn't work.
    I've already cancel the settings of "Allow HDR" and "Allow MSAA" on my camera, the result shows well on Unity Editor but not find any effection on my Android Device.

    And I wonder if I really need to use OnPostRender to optimize now? In fact I'd like to use HDR to make custom bloom, and it works on OnRenderImage well.
    I use the editor version 2018.3
     
    Last edited: Jul 15, 2019
  9. colin299

    colin299

    Joined:
    Sep 2, 2013
    Posts:
    181
    you should always write it in OnRenderImage first. if OnRenderImage is the performance problem, then record the time cost and start optimize it using other method, and only accept the optimization until you confirm the new method is faster.

    I have moved to SRP, so all these OnPreRender/OnPostRender method may or may not have any gain at all now, I just don't know.
     
  10. Reguluz

    Reguluz

    Joined:
    Nov 9, 2018
    Posts:
    5
    :(thanks for your reply.I'll try it