Noise generation with compute shaders problem
Hi, I am working on a project where I am using marching cubes for terrain generation. I use the FastNoiseLite library as input for my algorithm. To optimize my code, I started using compute shaders. This went well for my marching cubes algorithm but I ran into a problem when trying to implement this for my noise generation. Like the title says, my pc freezes for 2 seconds and afterwards the game crashes when I boot the game. My GPU (4070) spikes to 100% utilization.
I expect to get the parameters from the function and be able to return them. Instead, my pc freezes and than my game crashes (just the game, not the editor itself). I don't get any error messages when this happens
My code for the CPU side and my compute shader itself can be found below.
GDscript:
func get_per_chunk_params(chunk_coords: Vector3):
var resolution := chunk_size + 1
var total_voxels := resolution * resolution * resolution
#create output buffer
var output_bytes := PackedFloat32Array()
output_bytes.resize(total_voxels)
var output_buffer := rd.storage_buffer_create(output_bytes.size() * 4, output_bytes.to_byte_array())
var uniform := RDUniform.new()
uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
uniform.binding = 0
uniform.add_id(output_buffer)
var noise_generation_uniform_set = rd.uniform_set_create([uniform], noise_generation_shader, 0)
# Prepare push constants
var world_offset := chunk_coords * chunk_size
var push_constant := PackedFloat32Array([
world_offset.x, world_offset.y, world_offset.z,
iso_level,
float(chunk_size),
noise.frequency,
float(noise.noise_type),
float(noise.fractal_type),
float(noise.fractal_octaves),
noise.fractal_lacunarity,
noise.fractal_gain,
noise.fractal_weighted_strength,
noise.fractal_ping_pong_strength,
float(noise.seed),
##padding for now, you need to supply the compute shader with 64 bytes when using constants
0.0,
0.0
])
var compute_list := rd.compute_list_begin()
rd.compute_list_bind_compute_pipeline(compute_list, noise_generation_pipeline)
rd.compute_list_bind_uniform_set(compute_list, noise_generation_uniform_set, 0)
rd.compute_list_set_push_constant(compute_list, push_constant.to_byte_array(), push_constant.size() * 4)
rd.compute_list_dispatch(compute_list, resolution / 8, resolution / 8, resolution / 8)
rd.compute_list_end()
#submit and wait a frame before syncing
rd.submit()
await get_tree().process_frame
rd.sync()
var output_data := rd.buffer_get_data(output_buffer)
var params := output_data.to_float32_array()
safe_free_rid(noise_generation_uniform_set)
safe_free_rid(output_buffer)
assert(params != null, "Per_chunk_params should never be null")
return params
Compute shader:
#[compute]
#version 450
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
//output buffer for voxel data
layout(set = 0, binding = 0, std430) restrict buffer VoxelData {
float data[];
} voxel_data;
//push_constant parameters is a way to pass small amounts of data to the shader efficiently WITHOUT USING BUFFERS
//you need to supply the compute shader with 64 bytes when using constants
layout(push_constant, std430) uniform Params {
vec3 world_offset;
float iso_level;
int chunk_size;
// FastNoiseLite parameters
float frequency;
int noise_type;
int fractal_type;
int fractal_octaves;
float fractal_lacunarity;
float fractal_gain;
float fractal_weighted_strength;
float fractal_ping_pong_strength;
int seed;
} params;
//hash function for pseudo-random number generation
float hash(vec3 p) {
p = fract(p * 0.3183099 + 0.1);
p *= 17.0;
return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
}
//simplified Perlin-style noise
float noise3D(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
// Cubic interpolation
f = f * f * (3.0 - 2.0 * f);
// Hash corners
float n000 = hash(i + vec3(0.0, 0.0, 0.0));
float n100 = hash(i + vec3(1.0, 0.0, 0.0));
float n010 = hash(i + vec3(0.0, 1.0, 0.0));
float n110 = hash(i + vec3(1.0, 1.0, 0.0));
float n001 = hash(i + vec3(0.0, 0.0, 1.0));
float n101 = hash(i + vec3(1.0, 0.0, 1.0));
float n011 = hash(i + vec3(0.0, 1.0, 1.0));
float n111 = hash(i + vec3(1.0, 1.0, 1.0));
// Trilinear interpolation
float x00 = mix(n000, n100, f.x);
float x10 = mix(n010, n110, f.x);
float x01 = mix(n001, n101, f.x);
float x11 = mix(n011, n111, f.x);
float y0 = mix(x00, x10, f.y);
float y1 = mix(x01, x11, f.y);
return mix(y0, y1, f.z) * 2.0 - 1.0;
}
// FBM (Fractal Brownian Motion) for fractal noise
float fbm(vec3 p) {
float value = 0.0;
float amplitude = 1.0;
float frequency = params.frequency;
for (int i = 0; i < params.fractal_octaves; i++) {
value += amplitude * noise3D(p * frequency);
frequency *= params.fractal_lacunarity;
amplitude *= params.fractal_gain;
}
return value;
}
void main() {
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
int resolution = params.chunk_size + 1;
// Check bounds
if (pos.x >= resolution || pos.y >= resolution || pos.z >= resolution) {
return;
}
vec3 world_pos = params.world_offset + vec3(pos);
world_pos += vec3(float(params.seed) * 1337.0);
float value = fbm(world_pos);
// Calculate 1D index from 3D coordinates
int index = pos.x + pos.y * resolution + pos.z * resolution * resolution;
voxel_data.data[index] = value;
}#[compute]
#version 450
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in;
//output buffer for voxel data
layout(set = 0, binding = 0, std430) restrict buffer VoxelData {
float data[];
} voxel_data;
//push_constant parameters is a way to pass small amounts of data to the shader efficiently WITHOUT USING BUFFERS
//you need to supply the compute shader with 64 bytes when using constants
layout(push_constant, std430) uniform Params {
vec3 world_offset;
float iso_level;
int chunk_size;
// FastNoiseLite parameters
float frequency;
int noise_type;
int fractal_type;
int fractal_octaves;
float fractal_lacunarity;
float fractal_gain;
float fractal_weighted_strength;
float fractal_ping_pong_strength;
int seed;
} params;
//hash function for pseudo-random number generation
float hash(vec3 p) {
p = fract(p * 0.3183099 + 0.1);
p *= 17.0;
return fract(p.x * p.y * p.z * (p.x + p.y + p.z));
}
//simplified Perlin-style noise
float noise3D(vec3 p) {
vec3 i = floor(p);
vec3 f = fract(p);
// Cubic interpolation
f = f * f * (3.0 - 2.0 * f);
// Hash corners
float n000 = hash(i + vec3(0.0, 0.0, 0.0));
float n100 = hash(i + vec3(1.0, 0.0, 0.0));
float n010 = hash(i + vec3(0.0, 1.0, 0.0));
float n110 = hash(i + vec3(1.0, 1.0, 0.0));
float n001 = hash(i + vec3(0.0, 0.0, 1.0));
float n101 = hash(i + vec3(1.0, 0.0, 1.0));
float n011 = hash(i + vec3(0.0, 1.0, 1.0));
float n111 = hash(i + vec3(1.0, 1.0, 1.0));
// Trilinear interpolation
float x00 = mix(n000, n100, f.x);
float x10 = mix(n010, n110, f.x);
float x01 = mix(n001, n101, f.x);
float x11 = mix(n011, n111, f.x);
float y0 = mix(x00, x10, f.y);
float y1 = mix(x01, x11, f.y);
return mix(y0, y1, f.z) * 2.0 - 1.0;
}
// FBM (Fractal Brownian Motion) for fractal noise
float fbm(vec3 p) {
float value = 0.0;
float amplitude = 1.0;
float frequency = params.frequency;
for (int i = 0; i < params.fractal_octaves; i++) {
value += amplitude * noise3D(p * frequency);
frequency *= params.fractal_lacunarity;
amplitude *= params.fractal_gain;
}
return value;
}
void main() {
ivec3 pos = ivec3(gl_GlobalInvocationID.xyz);
int resolution = params.chunk_size + 1;
// Check bounds
if (pos.x >= resolution || pos.y >= resolution || pos.z >= resolution) {
return;
}
vec3 world_pos = params.world_offset + vec3(pos);
world_pos += vec3(float(params.seed) * 1337.0);
float value = fbm(world_pos);
// Calculate 1D index from 3D coordinates
int index = pos.x + pos.y * resolution + pos.z * resolution * resolution;
voxel_data.data[index] = value;
}
Any help would be appreciated.
Posted Dec 3, 2025, 2:01 AM UTC