Search Unity

Camera.Render() seems to trigger Canvas.SendWillRenderCanvases()

Discussion in 'Unity UI (uGUI) & TextMesh Pro' started by cowtrix, Mar 21, 2017.

  1. cowtrix

    cowtrix

    Joined:
    Oct 23, 2012
    Posts:
    279
    Hey there. I've got a background camera rendering out some meshes to a RenderTexture. There is also a complex uGUI doing stuff with the user. Both of these have to run in parallel. I've found through profiling that calling a Camera.Render() on this background camera triggers Canvas.SendWillRenderCanvases() on the rest of the UI.


    What in tarnation! The background camera is set to have an empty event mask, and a culling mask of only one layer where it renders what it needs to. There are no UI elements on this layer. Has anyone seen this before? How do you tell a camera that it doesn't need to care about UI in any way, shape or form?
     
  2. cowtrix

    cowtrix

    Joined:
    Oct 23, 2012
    Posts:
    279
    For anyone's interest, I did end up solving this problem with some reflection. Basically I got the delegate, cached the value, nulled the value, did the camera render, and then set the delegate again from the cached value.

    Code (CSharp):
    1. var canvasHackField = typeof(Canvas).GetField("willRenderCanvases", BindingFlags.NonPublic | BindingFlags.Static);
    2. var canvasHackObject = canvasHackField.GetValue(null);
    3. canvasHackField.SetValue(null, null);
    4. Camera.Render();
    5. canvasHackField.SetValue(null, canvasHackObject );
     
    Last edited: May 5, 2017
  3. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    331
    This is still an issue in 2017.3. Thanks @cowtrix for figuring out this hacky solution, though this really needs to be addressed officially. It makes no sense that this much performance is wasted - cameras that don't render any UI should not have to do expensive calculations on whether or not they will render canvases. I have a set-up with many cameras rendering to render textures, and this eats away 50 milliseconds per frame for nothing.
     
    Agent0023 likes this.
  4. MiFrilke

    MiFrilke

    Joined:
    Dec 14, 2016
    Posts:
    15
    We noticed the same issue in 2018.2.6f1 recently. It drastically impacts performance on consoles for us, so I'm happy to have found this workaround, thank you @cowtrix, I hope it still works in 2018.2.

    I've also reported this as a bug (ID 1090213), but have not heard back from QA for a few days.
     
    customphase likes this.
  5. MiFrilke

    MiFrilke

    Joined:
    Dec 14, 2016
    Posts:
    15
    We actually heard back from QA after a while and had a bit of back-and-forth about this issue (ID 1090213)
    The result was:

    "After contacting developers about it, this seems to be expected behaviour. Consider all the separate Camera.Render() calls as different screenshots - the UI has to be rendered.
    We're sorry that it's causing you inconveniences."

    Which seems absolutely ridiculous to me. This would imply Unity expects us to use Camera.Render() only for creating screenshots? If I want a screenshot that includes the UI, I can simply enable the UI layer in the CullingMask of my camera. But if I want to render literally anything else, why am I forced to endure this useless UIEvents.WillRenderCanvases call even though no canvases are actually rendered?
    Sure, there is the workaround Hack posted above, but this shouldn't be the only solution to such an inherently wrong behaviour.

    Attached profiler screenshot shows a simple scene with some dynamic UI and one disabled camera (in addition to the main one) that is rendered from the Update function of a custom script. You can see almost 1ms being wasted on stuff that is simply not needed. The project this profiler screenshot has been taken in is also attached (2018.3.0b12).
     

    Attached Files:

  6. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    331
    It is definitely ridiculous. The post I made at the beginning of this year is about a running project, so we have the same requirements to this day. The cameras only render depth to render textures for use in a custom shader, they will never have anything to do with UI. If it wasn't for cowtrix, Unity would not even have been viable for this project. It is such an easy thing to bypass and it hogs so much performance doing absolutely nothing. I see no reason why Unity is pulling the "intended behaviour" card here.
     
    MiFrilke likes this.
  7. mushdevstudio

    mushdevstudio

    Joined:
    May 9, 2014
    Posts:
    29
    When I disabled Pixel Perfect on the canvas the frame rate drop went away.
     
  8. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,406
    Camera.Render() calls so much crap.
    I have some that does 1ms while there's nothing to render.
     
  9. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    331
    I decided to replace it fully with custom draw calls using CommandBuffer.DrawMesh (or DrawMeshInstanced). Luckily this was possible in my project because the camera's only render a limited amount of meshes, so I don't need occlusion culling. Frustum culling is easy to set up with GeometryUtility.TestPlanesAABB.
     
  10. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,406
    Mind sharing how? I can't figure how to reproduce a proper rendering. I always end up with messed up matrices.
     
  11. Thomas-Mountainborn

    Thomas-Mountainborn

    Joined:
    Jun 11, 2015
    Posts:
    331
  12. LightStriker

    LightStriker

    Joined:
    Aug 3, 2013
    Posts:
    2,406
    Work if you don't have the notion of a near/far clip, frustum and perspective, which I need.