Realistic Terrain Generation

Realistic Terrain Generation

3M Downloads

Performance

whichonespink44 opened this issue ยท 26 comments

commented

Not sure what's happened, but generation is being weirdly slow sometimes. It's intermittent for me, but something's definitely going on. Other people have confirmed this in the forums.

We need to get one of those fancy profilers on the scene and see where the bottleneck is.

commented

I suspect recursive decorations storms, myself. I think Forge has put something in to fix them and sometimes it gets stuck.

commented

That would explain it if that's what's happening. Also on the decoration side of things, it occurred to me that each decoration calls its own Forge event, so if there are three tree-related deco objects assigned to a biome, the 'TREE' event gets called 3 times... so that might not be a good thing.

That wouldn't explain the lag with modded biomes though, because I've only re-decorated vanilla, so it might be the terrain and/or river stuff contributing like you mentioned earlier.

commented

I noticed some real bad lag today generating vanilla biomes in 1.9 whilst testing AbyssalCraft, consistant with lag I've seen recently in 1.7.10.

To describe it further, it's not a lag where chunks generate slowly but consistantly, it happens in bursts. It will seem like generation has completely locked up, sometimes for 10-15 secs, then the surrounding chunks will 'burst' load, then another freeze for 10-15 secs. It's more noticeable in 1.9 since chunks appear to load differently than in 1.7.10, but it's relatively the same.

I've seen this sort of thing happen when available memory is low and the JVM garbage collector craps out trying to free up memory that it cant, or when the heap becomes badly fragmented, but this isn't the case. This sort of thing happens right from the start, even when generating spawn.

commented

Yep, that matches up with what I've been experiencing as well.

commented

My memory tracker does not show anything unusual when it happens.

commented

Hrm... I never even thought about that. This is definately worth some experimentation, since mod overrides can be added.

The real trick is knowing which mods need an override (probably just the ones with big trees that are doing decorating), and finding an easy way of adding those overrides. Adding them to the forgeChunkLoading.cfg is probably undesirable since it probably has to be done manually, and just changing the defaults for all mods is even more undesirable. Perhaps there's a way in code.

commented

The fundamental problem, if I'm right, is a code issue. About two years ago the Forge people decided to go to ansynchronous chunk loading. That means main thread operations can't do things like "if (world.getBlock(x,y,x) == Block.dirt) startplacingtree();" because to do that check the chunk has to be loaded. Instead, you'd have to write a Runnable or the like to check for dirt and place a tree if appropriate and pass that to the chunkloader, which will call it when the chunk becomes available.

At least, that's how it should work; I don't know how Forge actually implemented it (documentation? I couldn't find it). Theoretically it's a vastly superior system (as in, allows massive multitasking) but it requires a lot more work to write for. Basically any multi-block decoration has to be broken up into the section that goes into each chunk. I was thinking the solution would be to write a chunk-splitter that is effectively a fake world and which stores changes into objects, one for each chunk, that can be applied to the objects as they come up.

commented

If you'll look at the Forge configs you'll see the likely source of the problem. Forge has a limit on the number of chunks a mod can "force" open. The problem is that the way anything with a lot of big trees works it will "force" open every single chunk in viewing range. I don't know how Forge resolves that issue, but that's your problem - the tree decorators need more chunks and Forge won't let them have it. They have to wait for Forge's asynchronous loader to serve it up.

commented

With regards to the new tree system, Zeno said:

int intX = chunkX + rand.nextInt(16) + 8; int intZ = chunkY + rand.nextInt(16) + 8;

Doesn't that push the trees into adjacent chunks 3/4 of the time? That's highly undesirable for performance.

I'm just using code that was already there, but that certainly doesn't mean it's right, so I'll definitely look into that.

commented

I'm not really noticing this in 0.7.0, so something in the new system you made is causing the chunks to load much slower. I did notice that when you press esc to open the pause menu, all the chunks generate at a normal speed, or whatever speed you set it to when using OptiFine. For example, if I set my chunk updates to 5, they still lag and actually appear slowly, but when you pull up the pause menu, they all show up as normal.

commented

I pulled the trees and the water/lava lakes back to rand.nextInt() and I'm still seeing the long pauses. Could be other decorations still, of course. I'm still thinking the +8 should go - I can't figure how it would be correct.

commented

Well, I don't understand it at all but the +8 turns out to be necessary. I took them out and now the game crashes with a long series of chunk generations going to the upper left.

commented

What exactly does the +8 do? Because I've seen other mods use +7 when doing similar things. I wonder why? Do you think the addend is arbitrary? Could we avoid crossing into different chunks if we used a lower number?

commented

+8 and +7 are different rounds for 1/2 a chunk (16 wide, but the highest number is 15). At this point I'm thinking it probably doesn't go into different chunks but I can't figure out how, unless the in-chunk coordinates are inverted.

commented

The other RTG decos that spatter are: Cacti, Flowers, JungleCacti, WaterGrass, and WildWheat. Probably none of those are dense enough for a chunk border to be noticeable, except Flowers in FlowerForest and FlowerField.

commented

Great detective work @Zeno410 ๐Ÿ‘

Ok, I kinda get what's going on behind-the-scenes, but I don't know enough about it to come up with any noise-related solutions.

However, here are the first two (probably crap) ideas that popped into my head:

  1. Does vanilla grass gen have this problem? If not, could we just use that instead? (net.minecraft.world.gen.feature.WorldGenTallGrass)
  2. If we can't use vanilla grass gen, could we rewrite WorldGenGrass to place 'patches' of grass instead of individual grass blocks? Would that get around the chunk boundaries thing? I'm just thinking... if trees, for example, don't have this splattering issue, could we rewrite grass to behave like trees so instead of splattering individual grass blocks all over the place, it would place pre-procedurally-generated grass 'patches' and generate them the way trees do.

(Ok, that last one almost certainly doesn't make sense, but maybe something in there will trigger another idea.)

commented

WorldGenTallGrass does the exact same spattering and I assume that's where it comes from. Trees don't spatter, but their leaves spreading creates the same kind of issues. I assume WorldGenGrass came up as the problem because grass is EVERYWHERE but you do get breaks in the trees sometimes.

commented

OK, on looking into the crash logs from pulling decos into the chunk the problem deco is WorldGenGrass, of all things. The problem is that it first picks a random spot in the chunk, and then picks a bunch of 2D offsets from that spot. This will always wander into adjacent chunks, and usually at least some into the corner neighbors which are particularly problematic.

I really, really think WorldGenGrass should stay in its chunk. If we just use random spots in the chunk, then we'll get chunk boundaries on grass distributions. So, I'm looking for suggestions. One possibility is to pass an array of biome assignments and have each biome only place grass on itself.

I suspect similar problems will arise with other decos and perhaps "only in biome" is not an acceptable response. The biotic blending is often desirable. Anybody have any suggestions for a noise approach rather than a Jackson Pollock (spattering) approach?

commented

I also now understand why +8 (into adjacent chunks) works better that +0 (stay in target chunk). Because of the spattering, with +0 all neighboring chunks can be affected, for up to nine chunks. +8 pushes the effect far enough that only 4 chunks can be affected.

commented

Also, how is rPopulatePreDecorate() getting called after decoration?!

It's in another chunk. A decoration goes over the border and MC generates that chunk which if it has a decoration that goes over into another chunk causes it to generate which if it has a decoration....

This is what I refer to as the "rabbit hole". You go in and you have no idea how deep it goes.

commented

@Zeno410 Not sure if that actually happens (maybe it does), but as I explained in the other thread, preDecorate isn't down there because of that (gets called after decoration), it's just down there because that's how the profiler sorts methods.

commented

Just so everyone has a general idea of performance in RTG, here's a ranking of the most resource intensive methods in the current dev branch, when generating a huge map (90k chunks with pregenspawn 150 from ACT) to get a good sample size:
image
What I found interesting is that provideChunk is actually first here, while at the start (and throughout most of the process), it was below decoration methods. Could that mean that it's a terrain gen problem in one of the biome on the outer part of the map?
Edit: I wrote that before reading Zeno's findings about WorldGenGrass, but I think the above is still useful info.
Edit2: I'll upload the huge map if someone wants to take a look.

commented

How does that deal with nested calls?

I think it only shows the time that each function itself takes (no calls), and it presents every RTG function. There's another type of data visualization that I posted in the prevous thread that may be better for what you talked about.

What about time in vanilla routines called by RTG routines?

Good point, I only enabled looking for things in the rtg.* package for this scan, I should probably post a full one.

commented

How does that deal with nested calls? LakePressure, for example, is called only via provideChunk, and generally getRiverStrength gets called that way as well. What about time in vanilla routines called by RTG routines?

commented

I've actually managed to stop up the rabbit hole of nested decoration calls. It does make startup and especially shutdown faster, but to my surprise and disappointment it doesn't stop the generation pauses. There's a problem with the method preventing repeat chunk decoration which I'm stopping by keeping a hashtable of generated chunk location but server ops don't like that kind of thing. (plus, I'm not currently saving it).

commented

Fixed in b8ee919 and ba9c0a9