3
\$\begingroup\$

I've been trying to create procedural generated 2d terrain (similar to a game like Terraria) using Simplex noise. I was able to get the noise function working, and I set up a grid of 16 by 16 tiles that uses the noise to generate the height of the terrain. The problem is that the grid generation is rather slow. I often need to wait 5 - 10 seconds for a somewhat small grid to generate.

My code looks like this:

var level; // height of the terrain
generate_simplex_octaves2D(__largestfeature, __persistance, seed); // generate octaves using __largestfeature

for (var gy = 0 ; gy < global.__gridheight ; gy++)
{
    for (var gx = 0 ; gx < global.__gridwidth ; gx++)
    {
        level = (global.__gridheight div 2) + (100*get_noise2D(gx/100, gy/100, __octaves, __frequencys, __amplitudes)); // get noise value at point (gx/100, gy/100)
        if (gy > level) // if below level
        {
            global.__testgrid[# gx,gy] = TILE_FULL; // set grid position to a full tile
        } else global.__testgrid[# gx,gy] = VOID; // set grid position to an empty tile

        if (global.__testgrid[# gx,gy] == TILE_FULL) tilemap_set(global.testmap, 1, gx, gy);
    }
}

This creates terrain that looks fine, it's just too slow. I want to be able to generate terrain in real-time to create an infinite landscape. After doing some research, I found that I could maybe use a compute shader to have the GPU help speed up the generation, but I am not sure how to implement a compute shader in my case. Would I calculate the noise in a shader, or generate a noise map using one? Or do something else to generate the terrain faster?

\$\endgroup\$
  • 1
    \$\begingroup\$ Welcome to the Game Dev Stack Exchange. How badly does your current solution scale - if you double the grid width, does the time double? Also, have you tried profiling your code yet? \$\endgroup\$ – Pikalek Feb 13 at 22:58
  • 2
    \$\begingroup\$ Note for when you profile: I see loop variables that can be lifted and an optimizing compiler would probably do that for you. Only profile Release mode code, Debug mode compiles won't give you any useful data. \$\endgroup\$ – Patrick Hughes Feb 14 at 18:55
  • \$\begingroup\$ After profiling my code, I found that a grid of 40 by 23 tiles took about 1725 ms to generate, and a grid of 80 by 45 tiles took about 7500 ms to generate. Profiling in release mode didn't make much of a difference, although I did find what was causing most of the slowdown. I'm still not sure if I should be using the GPU to help with generating the noise. \$\endgroup\$ – PepticPaladin Feb 14 at 23:11
  • \$\begingroup\$ @PepticPaladin The difference in the time per tile is less than a ms between the two test cases you gave & the times themselves seem reasonable. Is the problem that you're trying to generate more tiles than this all at once? \$\endgroup\$ – Pikalek Feb 15 at 3:54
  • \$\begingroup\$ @Pikalek I want to have infinite terrain stored in chunks, like Minecraft. I feel like pausing even just for a few seconds every time a new chunk is loaded would not be ideal, but I also haven't gotten around to creating the infinite world system yet, so I don't know how noticeable it would be in practice. \$\endgroup\$ – PepticPaladin Feb 15 at 4:39
2
\$\begingroup\$

I can see 2 possible reasons of bad performance:

  1. You have very big numbers in X and Y. Like 2000*1000. In this way you must lower that. You can make bigger areas to generate (like 10*10 squares instead of 1*1). Or you can regenerate many possibilities for squares of 50*50 and randomly select them.

  2. Or it can be very hard function: get_noise2D(gx/100, gy/100, __octaves, __frequencys, __amplitudes). You can use lighter version of this or just random generator.

\$\endgroup\$
1
\$\begingroup\$

I'm seeing get_noise2D() and tilemap_set() as potential problems here. You didn't write them (?), so you don't know what's happening inside them. The high likelihood is there are heap memory allocations (new operator) going in inside there and it's causing your system to really chug as the CPU pipeline stalls while it waits for those allocations to return from main memory.

What you really need to do here is write dummy versions of each of these functions (or replace them basically) without any logic inside, and see which of these causes the gen-time to shoot up. So try dummying one, then the other, and see which impacts duration. Hopefully, gutting the first function doesn't cause the remaining logic to crash, else you'll have to supply dummy outputs therefrom.

Your nested loop and conditionals therein are, in my experience, not enough to cause serious slowdowns, even though nesting loops and conditionals is generally something you want to keep to a bare minimum.

\$\endgroup\$
  • \$\begingroup\$ I did write get_noise2D, and tilemap_set is a built-in function that sets positions in a 2D array to a tile in a tilemap. I profiled my code, so I do know what's causing the slowdown, it's the get_noise function. I've been experimenting with generating smaller sections in pieces and putting them together to increase speeds, although I am still going to have to go back through the function and see what else I can do to speed it up. \$\endgroup\$ – PepticPaladin Feb 27 at 21:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.