Large number of actors in view causes slowdown (regardless of mesh complexity)

So in my game, players are able to build large bases. Each building piece (such as a wall or floor) is an actor with a static mesh component.

I’ve been running some performance tests for large player-built bases. What I’m finding is that if I set all the static meshes to none, I see a large performance improvement (not surprising).

However I’m also seeing that if I replace every mesh with a simple (1 poly) mesh, there is seemingly no change in performance than when using the complex meshes.

Does anyone know why, when using a simple mesh, there is still such large performance overhead?

Anyone know of any tips on how to reduce this performance hit?

Thanks

Not sure if this will help but you might try this.
NetUpdateFrequency=100 in the actor file in default properties.
maybe try a lower value in those actors extended items. So it don’t update so many times a second.

Drawcalls are a hardware limitation.
I recommend you watch Epic’s GDC talk about how they “hacked” together the fort/wall building system in Fortnite …

To make the game runnable they implemented a special shader to animate and render wall tiles in mesh coordinates, instead of computing them in world space, offloading majority of the heavy work to the GPU
(tho as a result Fortnite became excessively GPU hungry).

That trick allowed them to significantly reduce drawcalls in a map with around 50,000 interactive actors + 100 networked players.

1 Like

you’ll probably have to make some more tests and profiling.
is it causing issues on the GPU or in the CPU?
does it improve it you disable shadows for them?
does it improve if all objects are behind a wall? (i.e. still on the camera frustum but occluded)
does your actor class tick? does it improve if you disable the tick?

Hello dude, I am going to give you some tips on how I handle my custom maps, I use 0% of BSP and 100% of Static Meshes for the level geometry. I create my maps on 3d model package, and I separate in “groups”, whenever importing to UDK. I have what I call level geometry pieces and level props pieces.

The level geometry pieces like the name says, is what compounds the meshes on which the players will walk and collide. For these pieces I give them a large lightmapp resolution (512 or 1024), and these pieces have poly collision enabled, however I tend to make them only one face polygon. In example, a floor terrain model on which the player will walk, I split it in many small chunks, and export them one by one to UDK, then I blend them toghether in the editor.

So the more chunks I have, is better for performance, because it helps the engine to occlude better the chunks that are not in the view range of the player, so they will not render, thus giving good performance.

Also I use Precomputed Visibility (UDK | PrecomputedVisibility), and even if it says that’s mainly for mobile, however I tested on PC and it gave me a very good performance.

Another tip is for the level props (barrels, static vehicles, light poles, trees), I use a colision model created in a 3d package, however, for the props which the player will never touch them, I turn off collision, and give it a 0 lightmap resolution.

Also for background models, distant models I made them extremely low poly models, which also gives me a good performance.

About the Pawns (the enemies), in my game testmap which I will showcase soon here, I placed 25 enemies arround my map, they all spawn at the same time (kismet sequence actor factory spawn on level loaded), and the performance is still good, arround 45-60fps.

I hope this tips and tricks be helpful for you.

Cheers.

Try to store the meshes in an array, delete the meshes when the player is far, and re-spawn them when the player is near.

I’m using this system with houses in my game. The house is an actor from staticmeshactor, with arrays that contains meshes, lights, doors, pawns and others. When the player is out, the interior is deleted, and when the player touch the house, the interior is created. In this way I can have many houses with rich interiors, but only one with interior at the same time.

Also the houses has two versions for the mesh, one complete with interior walls, floors, etc, using 3 or 4 materials, and one only with the exterior and one material, using a texture atlas, to reduce drawcalls. And this last one has LODs.

Tick is disabled when the player is out, and enabled when the player enters. Also I can use a distance value to activate, but the tick is always enabled in that case. And with the distance activation, only the meshes visibles outside are spawned, the rest when the player touchs the house.

Perhaps for multiplayer is not a good option, but you can try it.

@ If your game runs the same speed whether using simple meshes or complex meshes, your issue is probably CPU bound. In my own game, I have hundreds or even thousands of characters, each one consisting of just two triangles. Adding 1000 characters added only 2000 triangles to draw, which shouldn’t make a difference at all, but my game slowed down to where it was unplayable. I was able to greatly improve performance in my game by deciding which actors need to be ticked. You might also get performance improvements by turning off ticking, collision, physics, etc. unless they are specifically needed at a specific time.

@udkultimate I used to put large lightmap textures on everything, but that ended up making my file size huge and it would take hours to build. Now I’m much more careful about my lightmap UVs and I get good results with small lightmap textures. My file sizes are much smaller and lighting builds go much faster. As for making the ground the player walks on, why not use the landscapesystem?

@CobaltUDK do you really delete things when the player leaves a house? Why not just use level streaming?

2 Likes

@CobaltUDK it’s easier when you have a notion of what is a house, what’s inside it, etc. for it’s player-made walls and doors so you don’t get to have any such relationship between the actors.
I also spawn hundreds of staticmeshactors btw, every wall, ceiling, door, floor and every prop in my dungeons is modular. they are all present in the level and it costs nothing because they are not ticking, they get their collision disabled, and they are distance-culled, I have no need to destroy and re-spawn them.

2 Likes

Nathaniel, I use streaming levels too, but for example the city has 1 * 1.5 Km and its very dense. Most buildings are placing using the foliage tool, but I expect to have almost 200 houses with interior and I don’t want to hide/unhide sectors of the city using stream levels. I want to have good views of the city…

, in my case the static meshes has some impact in the performance, for example 1000 houses (only the static mesh) with a maxdrawdistance of 50000, without collision, and the player very far of them, takes 0.4 ms in the cpu thread. If I have 200 dynamic houses with 100 static meshes each, I have 20000 static meshes, 1 ms…

The thing is worse yet, the houses also contains dynamic actors: kactors, lights, bots, pickable objetcts, usable items… and those have a big impact in the performance.

Also I did some tests time ago with the script for the foliage. I deactivated the function to remove the far grass meshes, and that meshes had maxdrawdistance, no collision, no shadows… So when the player was moving, more grass, without limit. Only the near grass was visible. But the performance was worse when increase the grass count. With 50000 meshes was very slow, and only about 1500 were visible. And the performance was checked with player without moving, so the script does nothing, no spawn new grass. The performance was only due the large number of static meshes with maxdrawdistance.

About the question from perhaps the problem is the maxdrawdistance. Asuming that the actor has no tick, no shadows, no collision, I think the engine is checking in every tick what actors are in range to display or hide. That can be the problem.

The actor hidden with maxdrawdistance is not equal that the actor with a sethidden(true).

I learn that with the foliage tool clusters. There are a very big improvement if you hide the far clusters with sethidden(true), instead leave this task to the maxdradistance.

Perhaps the actors with sethidden are placed in a different array, and the engine only check the maxdrawdistance of the sethidden(false) actors. Perhaps instead distributing the check along several ticks, the engine checks all in every tick. I don’t know.

You can make a simple function to test that, apply a setthiden(true) for every actor for the building actors and see if there are any difference with maxdrawdistance. And tell us the result.

I did the test, the improvement was not much. I set the map to load all the streams levels, and with a cull distance volume to set the maxdrawdistance for all, and with the player in a side of the map, the function did a setthiden(true) over 9000 static meshes. The gain in the cpu thread was 0.2 ms, and the fps from 162 to 172.

With more static meshes probably will be a good deal.