Artifacts When Vsync Off

Godot Version

4.5

Question

This question is heavy on compute shaders but I don’t think they actually play a role, I just describe completely what I do.

I made a little ball simulation. We have a world scene that contains a Camera2D which contains a Sprite2D. We use a compute shader for our little simulation to generate a texture for the Sprite2D (with a 25px buffer on each side so texture is slightly bigger than cam). This means we basically do:

(sorry for shit formatting but this editor sucks hard, somehow doesnt allow me to intend anything)

func create_texture():
var tformat := RDTextureFormat.new()
tformat.width = viewport_size[0] + 2 * buffer_size # your texture width
tformat.height = viewport_size[1] + 2 * buffer_size # your texture height
tformat.format = RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM
tformat.usage_bits = RenderingDevice.TEXTURE_USAGE_STORAGE_BIT | RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT | RenderingDevice.TEXTURE_USAGE_SAMPLING_BIT

var tview := RDTextureView.new()
var texture_rid = rd.texture_create(tformat, tview)

var texture = Texture2DRD.new()
texture.texture_rd_rid = texture_rid

var tex_uniform := RDUniform.new()
tex_uniform.uniform_type = RenderingDevice.UNIFORM_TYPE_IMAGE
tex_uniform.binding = 1
tex_uniform.add_id(texture_rid)

return [tex_uniform, texture]


We send the texture to the shader and send the image to it:
imageStore(output_texture, pixel, color);

Note: This requires us to use the global RenderDevice (otherwise you can’t send textures to Texture2DRD).

Anyway, here’s the result: (there’s a fps label in the middle)

Vsync on: https://imgur.com/vGRNs8W

Vsync off: https://imgur.com/dpnN4iB

I’m curious as to why we see these jittery artifacts. I doubt it has anything to do with the compute shader. My refresh rate of my laptop monitor is 60hz and I use:

func _physics_process(delta):
# Run compute shader to update ball physics and render
var compute_list = rd.compute_list_begin()
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)

# Dispatch enough work groups to cover all pixels (local_size is 16x16)
var texture_width = viewport_size.x + 2 * buffer_size
var texture_height = viewport_size.y + 2 * buffer_size
var workgroups_x = (texture_width + 15) / 16
var workgroups_y = (texture_height + 15) / 16
rd.compute_list_dispatch(compute_list, workgroups_x, workgroups_y, 1)

rd.compute_list_end()

Which is set to do 60 calls/s to the shader.

So we generate 60 fps and have a refresh rate of 60hz.

Can someone tell me what’s going on here exactly?