Well, when you do things proper the instance of the vegetation you are on is related to the level tile you load in.
If at 4km^2 you add enough vegetation to crash the system, then you can probably try 1/4 the size of that, 2km^2.
That way the number of instaces is probably around 1/4 as well…
Distant levels are supposed to use mesh impostors as an aggregate or just paint the vegetation down into a texture - depends on the situation.
There are some wild examples oout there like Whicher3 which through some IRL magic manages to pull of tiles of 1k and still show/aggregate the diatant landacape without it costing more drawcalls than the engine can manage.
As always. What your are looking for is a nice balance of Tris On Screen to Drawcalls Count.
For instance, if you make a landacape of 144km^2 with 1km^2 tiles,
I can guarantee (from direct tests) that even impostors rendering while at the corner of a landacape looking over to the other corner (so all 144*144 tiles loaded as a drawcall) you get about 5fps…
That’s because 20,736 calls are used on landscape, and the tile you are on still costs you whatever your tiles happen to cost.
Octohedral rendering and HLOD aggregations become really important at that point.
The engine never offered a built in way to do either, nor do I think it ever will (though Nanite mesh shows some promise).
So you are on your own on figuring out how to best deal with that for your specific project…
Maybe - a mesh replacement approach to the whole landacape is ideal.
There are tons of ways to make the same generation/math of a know set of tris be cheaper, angle aggregation, packing lines to process, etc.
Its why Nanite is probably a good idea - you can really lessen lots of computation by just changing the way the math is sent to the GPU for calculation (on that note, you can make the math smarter too by preventing overly complex calculations or by making assumptions on a data set).
On the other hand, as far as drawcalls go - that system will always stick you into a transactional metod, where unless you modify things before they are sent to the gfx for computation you cannot reduce the load…
Either way, until Nanite does it, this isn’t something the engine can do.
Even when nanite will work, the rest of the engine is so bad that I don’t think it’s worth a dev team’s effort when much stronger, free competitors exist and provide much better customer care/and overall product.
Anyway…
Tl:DR;
Don’t use unreal.