Search Unity

Outdoor scene CPU rendering bottleneck

Discussion in 'General Graphics' started by Xine27, Mar 28, 2022.

  1. Xine27

    Xine27

    Joined:
    Feb 20, 2019
    Posts:
    11
    Hi everyone,

    As the title states, I'm running into a pretty substantial bottleneck with my game. The game has a large outdoor scene with thousands of trees and bushes. The foliage in general has lots of transparency on it, which likely makes shadow calculations and occlusion much more difficult. There is a single directional light in the scene (sunlight), with indirect lighting coming from the procedural skybox. I am using the default rendering pipeline.

    At this point, FPS in the editor often drops to about 15-20 FPS, which seems to be down to almost exclusively CPU rendering time. Physics and scripts are negligible, the GPU time would get me to about 90 FPS. Camera.render on the CPU takes about 70% of the time, of which 42% is in drawing (largest part being 14% in Shadows.RenderShadowMap, with another 8% in Shadows.PrepareShadowmap), and about 13% is in culling.

    I am using forward rendering since it seemed to be the best call for a scene with a single light and lots of transparency, but using deferred rendering improves performance a little. Still, Camera.render is the highest percentage hit, with about 13% in culling, and about 45% in drawing (again with Shadows.PrepareShadowmap being around 8-9%).

    Considering the size of the scene and the fact that the player can cut down trees and bushes, I am thinking that creating a lightmap probably isn't a viable option. I've read up a little about light probes, but I am not sure if it would improve performance, or simply improve quality. I also know that there are assets on the store that can move a decent amount of the culling to the GPU side, but I have yet to look into them further.

    I would appreciate any advice and recommendations, I am still a little out of my depth with all things light and rendering.

    Thank you!
     
  2. Roggi_

    Roggi_

    Joined:
    May 14, 2014
    Posts:
    84
    Baking lighting for such a scene is I think impossible. Your best bet is to utilize some dynamic aproach, like SSGI or some assets.

    As for optimization, Unity by itself does nothing for scenes like this. I've previsouly optimized a scene exactly like yours. About 2x2km, mostly just forest area.
    Even though most trees are surely just instances of the same prefab/model, unity handles this very poorly. In a scene where 90% of the trees are merely present in the scene but not visible, they still take a great deal of memory (especially VRAM) and cpu time. What worked for me was to cache transforms (position, rotation, scale) of every single tree object into a scriptable object and then write a custom object pooler that places an instance of a tree where it should be, in a radius around the player camera.

    To avoid calculating distances for each tree to the camera I divided the map into 8x8 grid and noted which tree belongs to which chunk. I calculate the grid position of the camera and hide/show (place from pool) the grid chunks around the player.
     
    Xine27 likes this.
  3. Raul_T

    Raul_T

    Joined:
    Jan 10, 2015
    Posts:
    363
    Hey. I've answered a similar post a while back with some things that helped with the production of our now released game.

    Maybe it helps:
    Unity hits 15 fps with an 8192 meter terrain and 20k trees and rocks (gameobjects). - Unity Forum
     
    Xine27 likes this.
  4. Xine27

    Xine27

    Joined:
    Feb 20, 2019
    Posts:
    11
    Hi Roggi_ and Raul_T,

    Thank you both for your answers! I really appreciate knowing that we're all going through the same process with large outdoor forests...

    I'd been looking at the GPU Instancer for a while, so I think I'll finally just pull the trigger on that - hopefully I can get around writing yet another big back-end system with that, though I may just have to write some sort of object pooler anyway to handle the physics that happen outside the player's area of influence.

    @Raul_T - I see that you can cut trees down in your game as well - I assume you just de-instanced them with a collider as the player got close enough?
     
  5. Raul_T

    Raul_T

    Joined:
    Jan 10, 2015
    Posts:
    363
    All our loaded trees have capsule colliders. We verify if something is a tree by checking if the collider is in the NatureTrees layer we have. When we cut them we de-instance the gameobject from GPUinstancer and we destroy the tree gameobject (collider, attached interactions if any - some trees have mushrooms that can be pickup etc)

    There are other ways to do it with GPUInstancer, ideally, a non game-object approach for trees where you render them without a scene object and you pool colliders in your close proximity only, would be faster and less memory intensive but we optimized this late in development and we had to work with what we already had in game.
     
  6. Xine27

    Xine27

    Joined:
    Feb 20, 2019
    Posts:
    11
    @Raul_T I implemented the GPUI in much the same way as you have for the trees and some other objects - instantly a massive improvement. Pooling colliders isn't really an option for us at this point, considering we have villagers that are acting across the map and need to interact with trees as well, but the performance is already much better.

    Thank you very much for your advice, I really appreciate it!
     
    tmonestudio and Raul_T like this.
  7. Raul_T

    Raul_T

    Joined:
    Jan 10, 2015
    Posts:
    363
    Awesome to hear! Good luck with your project!