Search Unity

Better control of load/store actions for Camera

Discussion in 'iOS and tvOS' started by Alexey, Oct 23, 2018.

  1. Alexey

    Alexey

    Unity Technologies

    Joined:
    May 10, 2010
    Posts:
    1,624
    If you are using "legacy" rendering pipeline (not SRP) then Camera component is the one making all the choices about load/store flags of its target texture. If you render to actual RenderTexture you can use RenderTexture.DiscardContents to tweak load/store actions [1].
    But, alas, there is no way to do so for backbuffer (and you might also say that doing the right combination of DiscardContents is akin some magic). So what is preferred is for you to setup render target yourself, but this is also not that straightforward. The "natural" place would be in OnPreRender, but after that and before actually rendering scene, camera will render shadows (or depth texture https://docs.unity3d.com/Manual/SL-CameraDepthTexture.html in general). So you need to setup render target after shadows but before actual rendering.
    Enter CommandBuffer [https://docs.unity3d.com/Manual/GraphicsCommandBuffers.html] API. Assuming you use forward rendering you need BeforeForwardOpaque camera event. But there is yet another caveat: if camera has clear flags set - clear will happen BEFORE BeforeForwardOpaque event (and that will setup render target). So the full solution would be:
    1. make camera "Don't clear" [CameraClearFlags.Nothing]
    2. add command buffer for BeforeForwardOpaque event
    3. in there set rendertarget to the one you want with appropriate load/store flags
    4. do an explicit clear yourself. Be warned: you *must* clear depth. [2]

    bringing this all together, this is sample code (setting backbuffer, discarding depth after rendering, clearing color/depth):

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Rendering   = UnityEngine.Rendering;
    5. using LoadAction  = UnityEngine.Rendering.RenderBufferLoadAction;
    6. using StoreAction = UnityEngine.Rendering.RenderBufferStoreAction;
    7. public class TestSetRenderTarget : MonoBehaviour {
    8.     void Start() {
    9.         Rendering.CommandBuffer cb = new Rendering.CommandBuffer();
    10.         cb.SetRenderTarget(Rendering.BuiltinRenderTextureType.CameraTarget,
    11.             LoadAction.DontCare, StoreAction.Store, LoadAction.DontCare, StoreAction.DontCare
    12.         );
    13.         cb.ClearRenderTarget(true,true, new Color(1,0,0,1), 1.0f);
    14.         GetComponent<Camera>().AddCommandBuffer(Rendering.CameraEvent.BeforeForwardOpaque, cb);
    15.     }
    16. }

    [1] RenderTexture.DiscardContents will do different things depending on whether it is current render target: if it is, storeAction will be DontCare, otherwise loadAction will be DontCare.
    [2] Internally unity will delay setting render target until actual draw call, so those two calls (set render target and clear) will be combined, i.e. MTLRenderPassAttachmentDescriptor.loadAction will be set to MTLLoadActionClear.
     
  2. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    I’m not quite understanding what the practical purpose of this is, could you elaborate a bit?
     
  3. ViniciusGraciano

    ViniciusGraciano

    Joined:
    May 19, 2013
    Posts:
    13
    This saves A LOT of memory bandwidth when running on tile-based GPUs such as the ones in mobile devices. Also, this solution was posted as an answer to this thread.
     
  4. User340

    User340

    Joined:
    Feb 28, 2007
    Posts:
    3,001
    Is this something that every developer should do or is it just for specific use cases?
     
    protopop likes this.
  5. johnfrog

    johnfrog

    Joined:
    May 7, 2015
    Posts:
    13
    I asked this question in another thread but probably better to ask here:

    @Alexey - how can we achieve this for a memoryless MSAA back buffer?

    I've used your command buffer code above and set the store action to Resolve and modified the MetalHelper.mm file to set MSAA texture to MTLStorageModeMemoryless and it's resolve texture to MTLStorageModeShared.

    https://imgur.com/a/XlT5Cey

    Still doesn't seem to work though. Store action is still store.

    See 20 mins in on this video https://developer.apple.com/videos/play/wwdc2019/606/ for what I'm trying to do.

    I could reduce bandwidth a lot if I could get this working.

    Cheers
     
  6. Alexey

    Alexey

    Unity Technologies

    Joined:
    May 10, 2010
    Posts:
    1,624
    for 2020.1 we decided that we had enough and we fixed this "end of frame facepalm" - if you have MSAA color it will be (simply, w/o store) resolved to drawable, and depth/stencil will be discarded. We will backport to 2019 LTS too after 2019.3 release
    @johnfrog is it what you asked about? (i will recheck interop with memoryless just in case)
     
  7. johnfrog

    johnfrog

    Joined:
    May 7, 2015
    Posts:
    13
    Apologies for super late reply. Yeh - basically want to get rid of the store action. We're still on 2018.4.8f1 - bit scared to update with the amount of users we currently have in prod. On the larger devices i'm seeing 22mb allocated for that MSAA texture - and it's stuck on a store action :(
    Would love to get it on to tile memory but i just don't think it's possible on our version.
     
  8. Alexey

    Alexey

    Unity Technologies

    Joined:
    May 10, 2010
    Posts:
    1,624
    >>We're still on 2018.4.8f1
    hm, i guess we might look into backporting (but that's equally scary for us 8))
     
  9. johnfrog

    johnfrog

    Joined:
    May 7, 2015
    Posts:
    13
    Well - i managed to get this working today by overriding the objc runtime. This wasn't an issue for us since no where in our pipeline do we actually need to store and multisample resolve - so i just routed it to multisample resolve and made the target rt memoryless. Similar thing to what you guys do for heap storageMode for MTLHeapDescriptorClass. Saved 22mb store in the bandwidth which was very nice!
     
  10. tangwilliam

    tangwilliam

    Joined:
    May 4, 2016
    Posts:
    22
    Why should I do Clear by hand instead of Setting Camera's flag to SolidColor ?
    In my case, the last rendering camera is UI Camera, which renders to back buffer. I tests cb.ClearRenderTarget and Camera.ClearFlag = SolidColor, both get the same dependency view and the same bandwidth on XCode.
     
  11. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    @johnfrog would you mind elaborating on what you did? It would be much appreciated!!
     
  12. joshuacwilde

    joshuacwilde

    Joined:
    Feb 4, 2018
    Posts:
    731
    Was this backported already? We are on 2019.4.

    For reference, this is what I am seeing in Xcode GPU Frame Capture :
     

    Attached Files:

    Last edited: Feb 10, 2021