OnRenderImage() is SLOW when MSAA is on

In my test on android (galaxy note2, GLES2.0),
OnRenderImage() always slower than OnPreRender+OnPostRender method

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.

1 Like

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™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.

3 Likes

Could someone from unity confirm this. I also tested and get same results.

Anyone interested to test the difference betweeen OnRenderImage() & OnPreRender+OnPostRender(), here is a package to test.

using UnityEngine;
using System.Collections;

public class OnPrePostRender : MonoBehaviour
{
    public Shader ImageEffectShader;
    Material _mat;
    Material Mat
    {
        get
        {
            if (_mat == null)
                _mat = new Material(ImageEffectShader) { hideFlags = HideFlags.DontSave };
            return _mat;
        }
    }
    public enum MSAA
    {
        _1 = 1,
        _2 = 2,
        _4 = 4,
        _8 = 8
    }
    public MSAA _MSAA;
    RenderTexture mainRT;
    void Start()
    {

    }
    void OnPreRender()
    {
        mainRT = RenderTexture.GetTemporary(Screen.width, Screen.height, 24, RenderTextureFormat.Default, RenderTextureReadWrite.Default, (int)_MSAA);
        GetComponent<Camera>().targetTexture = mainRT;
    }

    void OnPostRender()
    {
        GetComponent<Camera>().targetTexture = null;
        Graphics.Blit(mainRT, null, Mat);
        RenderTexture.ReleaseTemporary(mainRT);
    }
}
using UnityEngine;
using System.Collections;

public class OnRenderImageTest : MonoBehaviour
{
    public Shader ImageEffectShader;
    Material _mat;
    Material Mat
    {
        get
        {
            if (_mat == null)
                _mat = new Material(ImageEffectShader) { hideFlags = HideFlags.DontSave };
            return _mat;
        }
    }

    private void OnRenderImage(RenderTexture src,RenderTexture dest)
    {
        Graphics.Blit(src, dest, Mat);
    }
}

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

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

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

case3: Adreno™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.

2762498–199489–OnRenderImagePerformanceTest.unitypackage (15.8 KB)

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.

3 Likes

So is this fixed?

3026723--226280--OnPreRender_OnPostRenderWithMSAA.jpg 3026723--226281--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.

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

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.

:(thanks for your reply.I’ll try it