|
|
|
|
Reply From: |
Mario |
I didn’t perform any actual benchmarking on this, but looking at the engine’s source code, it’s easy to make a few assumptions explaining this with both function call overhead as well as memory usage. If we look at the first few lines of the implementation for get_image()
, we’ll see that the generated/passed image data is a (grayscale) image using 1 byte per pixel:
Ref<Image> OpenSimplexNoise::get_image(int p_width, int p_height) const {
Vector<uint8_t> data;
data.resize(p_width * p_height);
uint8_t *wd8 = data.ptrw();
Doing the simple multiplication (and ignoring extra overhead from classes/references), this means we’ll be working with roughly 1 MB of data allocated in one go (this is oversimplification here, though). To actually fill the data, the C++ part will call the very same get_noise_2d()
function a million times, too. However, the GDScript side will never see those 1 MB, only the reference (4 or 8 bytes based on architecture).
Now if you go with the million calls from GDScript you should expect a difference, even from memory alone, ignoring extra overhead from calling. get_noise_2d()
returns a float
. While the size of this datatype might change between implementations/platforms etc. you can generally assume that it’s nowadays at least 32 bits (or 4 bytes) long. This means we’re passing 4 bytes per “pixel” resulting in 4 MB of data.
So data wise – only talking about the information moving between the raw C++ part and the script runtime – we’re looking at a factor of 1 million (4 MB vs. 4 byte). As such, having an actual factor of around 2 isn’t that bad overall (mostly thanks to fast memory these days I guess).
But besides that, this totally sounds like a typical case of “you’re (probably) doing it wrong”. If you’re trying to generate a huge or nearly infinite terrain, you should definitely split it up into individual “chunks”, generating them on the fly and only while needed (e.g. close to the player). This will significantly reduce generation time, lower overall memory usage and improve your framerate, too.
If this doesn’t apply to you and you’re trying to do something else, I’m still sticking with “you’re (probably) doing it wrong”, but we’d need a few more details.
But get_image
calls also 1000x1000 times to get_noise_2d
which returns a float
so that memory difference is not correct because happens on my GDScript snippet and in the source. But I reckon, as @omggomb pointed, that the communitation between native and GDScript is enough to explain the difference.
About the second question, I’m open for suggestion. To be fair I haven’t done much research on good techniques, just experimenting on my own. Currently it works like this: there are chunks that are generated and deleted dinamically depending on the position of the player. Each chunk has a position and a noise generator OpenSimplexNoise
. But then if each chunk is 100 pixel size, the Chunk (0,0) should call OpenSimplexNoise.get_noise_2d(x,y)
from (0,0) to (100, 100), but the Chunk (1, 0) from (100, 0, to (200, 100).
That’s the reason why I can’t use the OpenSimplexNoise.get_image
function, because then the noise would just repeat again in every Chunk with the same noise properties. I could generate a 200x200 image and just use the part I’m interested, but this would grow very quick as I get away from the origin.
One workaround I’ve been thinking is to just make every Chunk has a different seed, generate the image by get_image
and average the borders with the neighbours so it looks smooth on the edges of each chunk.