Search Unity

FPS reduces after combining the meshes, but I can't think of a reason

Discussion in 'General Graphics' started by marcos4503, Jun 14, 2021.

  1. marcos4503

    marcos4503

    Joined:
    Nov 1, 2015
    Posts:
    76
    Hey

    I'm going through a strange case in an optimization process, but I can't understand what could be causing this...

    I have some separate meshes that form a little scene. This little scene fits entirely on the screen, the camera doesn't move and there are no scripts in this scene.

    There are approximately 211 meshes in this small scenario, and after combining them, the result is only 1 mesh. In this scenario there are a total of 2,578,000 vertices and this number does not change even after combining all the meshes. The difference is that after merging, there is only 1 mesh with 2,578,000 vertices left.

    Before merging the scene is generating 1059 Batches and after combining all meshes there are only 100 Batches left. I understand that the amount of vertices is high, and I have a GTX 1060 6GB. It turns out that before merging I had 170 FPS and after merging this number decreases to 120 FPS.

    A little detail: In this merge I don't combine the textures.

    I don't understand why this happens even with the number of Batches decreasing so much, and all these GameObjects/Meshes being combined into one...

    I would be grateful if anyone could offer a clue about this!
    Thank you so much for your time! :)
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    I have two guesses: shadows and/or sorting order.

    Shadows:
    Are you using any lights with real time shadows? And are you quoting the vertex count that shows in the Game view's Stat's window or from someplace else? If that vertex count isn't from the stats window and you do have real time shadows, merging the mesh can have a serious effect on performance as now the entire mesh is being rendered for every shadow map. Directional lights can have up to 4 separate shadow maps (one for each cascade), and point lights have 6 (one for each side of a cube). If your mesh is separated, the meshes that are not visible to each shadow map "view" can be culled.

    However this should affect the stats window's vertex count, so if that number really isn't changing, this isn't it.


    Sorting Order:
    This one is a bit more subtle as there's not a good way to visualize this within Unity. But the possible issue here is high overshading of opaque objects.

    Quick breakdown of how GPUs render opaque objects. All modern GPUs use a depth buffer (aka z buffer) for opaque surface sorting and hidden surface rejection. The idea is no matter what order you render opaque objects the final image will be exactly the same. This is accomplished by calculating the depth from the view plane at each pixel when rendering an opaque object. This depth is then compared to the value in the depth buffer, and if the current pixel is closer than the value in the depth buffer it renders that surface and updates the depth buffer. If it's further away, it skips rendering that pixel.

    While the order doesn't matter for the final image, it does for rendering optimization. Any time a pixel is rendered to more than once is wasted GPU resources, as only the "last" time that pixel is rendered to appears in the final image. This is what's known as overshading. To minimize this you want to try to render opaque surfaces closest to the camera first. If you do that perfectly, then there'll be almost zero overshading leading to more efficient usage of the GPU. In reality it's basically impossible to perfectly sort surfaces, and sorting per triangle in real time is expensive. Even if you did sort per triangle, there's no "perfect" sorting for intersecting geometry, and there's more complex stuff like vertex cache coherency, pixel quad rendering, and wavefront / warp utilization that further complicates the matter. So the goal is really to minimize overshading rather than entirely avoid it. And do your sorting in a way that's not more expensive than the time you're saving avoiding overshading.

    Unity's approach (as well as most real time engines) is to simply sort individual objects by distance from the camera rather than sorting individual triangles. This alone is enough to get rid of most overshading problems without being heinously expensive.

    However if you've merged your meshes into a single object Unity can't sort them anymore. If you want to see this in action, you'll need to download RenderDoc and use its "Quad Overdraw (Pass)" overlay. Unity's Scene view has an Overdraw view mode ... but this is useless for determining overshading, as it just draws everything with a transparent additive shader to show where geometry is overlapping, but does not take into account depth rejection.

    You can try to fix this by manually sorting the meshes prior to merging them. Or you can go really hardcore and try sorting the triangles individually (though I might recommend running
    Mesh.Optimize()
    afterwards even though it will increase overshading as that'll reorder the triangles again slightly). If you're using
    Mesh.CombineMeshes()
    , you can sort the
    CombineInstances
    based on the depth to the center of the mesh bounds, which is how Unity does it's sorting.
     
  3. marcos4503

    marcos4503

    Joined:
    Nov 1, 2015
    Posts:
    76
    Whoa! Thank you very much for the great and super detailed answer! :)
    I really appreciate this!

    Apparently the cause of the problems with the combined mesh was the Realtime Shadows! When I turned off my Editor Realtime Shadows, the scene was running at 1300 FPS (without the mesh resulting from the merge). After combining all the meshes for testing, the scene run at 1800 FPS!

    I really appreciate the great response! I tried to look for something like this or materials that could answer my questions, on the internet, but I couldn't find it anywhere...
    I knew that disabling Shadows always brings performance gains, I just didn't know they caused FPS losses in very large combined meshes!

    Again, thank you so much and thank you for your time! :)
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,352
    While I was thinking about it, FPS is a terrible metric for gauging performance. The difference between 1300 fps and 1800 fps is 0.2 ms. That's the same difference as between 60 fps (16.667 ms) and 60.7 fps (16.467 ms).

    Really 211 meshes isn't a lot. 1059 isn't a lot. There's not a huge reason to try to optimize away 1000 batches.
     
  5. marcos4503

    marcos4503

    Joined:
    Nov 1, 2015
    Posts:
    76
    I understand!

    I really understand that the time difference between 1300 FPS and 1800 FPS is very low, but because the FPS went down instead of up, after doing the merge, I understood something was wrong, so I really needed to understand that better, even to learn more about the Engine!

    The scene I've also created is for mobile, and the game isn't in the optimization stage yet, but I needed to test the mesh-combination optimization, so I came across this question haha!
     
  6. UnityLighting

    UnityLighting

    Joined:
    Mar 31, 2015
    Posts:
    3,875
    For mobiles you can use separated meshes (not combined) and use the distance based culling feature of the camera.... it's much better than combining models for mobiles
    Follow below link for full detail about optimization:
    https://forum.unity.com/threads/unity-5-to-unity-2021-android-performance-tips.437695/
     
    marcos4503 likes this.