The terrain itself is generated with Godot's FastNoiseLite during load time.
For the unit ground 'collision' I created a job that uses bilinear interpolations of the surrounding heights to adjust the unit position.
For the terrain destruction, the vertex positions get updated on the CPU via a job. The difficult part was updating the mesh. I did multiple attempts to get this working fast enough.
The first attempt was creating a new mesh each time, but this was slow (14ms) and allocated both native and managed memory each time.
Second attempt was using RenderServer.mesh_surface_update_vertex_region but I couldn't figure out how to send the vertex normals with RenderServer.mesh_surface_update_attribute_region. I gave up on this.
Third attempt I created a 32bit floating point ImageTexture and set the data with the float[] of the terrain heights.
In the vertex shader I read the height and adjust the vertex position. Then vertex shader calculates the vertex normal using neighbouring height texture samples. This works great! Whole terrain updates in 0.6 ms. (Bonus: the height texture can be used for all kinds of effects later)
I've also added blob shadows to all the units so they are easier to see.