Zylann / godot_voxel

Voxel module for Godot Engine
MIT License
2.59k stars 245 forks source link

Creating physics shapes is extremely slow #54

Closed Zylann closed 5 years ago

Zylann commented 5 years ago

To put things in context, this module relies on creating a lot of unique meshes at runtime (in the hundreds). Most of it is done inside threads, but a small part is done on the main thread at a limited pace to prevent game freezing. Unfortunately, with physics turned on, Godot-related overhead is murdering that phase's performance, making the game really slow to react to dynamically loading and changing terrain (would still happen if it was done in a thread though because it's just too slow).

Creating a collision mesh is 20 times slower than creating the visual mesh. Optimizing this is important even for single edits, as they aren't as snappy as they can be.

This graph says it all, for a SINGLE 32x32x32 voxel block:

Debug build image

Release_Tools build: image

Debug takes 80 ms, release takes 8 ms, but overall the relative timings remain the same. There is room to at least double the speed of this.

Copying from #48: Creating the physics mesh is litterally asking VisualServer for the vertices, which further destroys framerate not only because it triggers again the wait for the render thread, but also because it might pull the data back from the graphics card. I hope I'm wrong about the latter... Those vertices are generated for unique voxel meshes so the cache in Mesh is also useless. But more importantly, since they are in RAM already (since I generate them), there is no point using VisualServer for anything physics-related.

Also Godot builds a BVH when TriangleMesh is created, which is completely useless when Bullet is used anyways. It basically takes almost HALF the time doing this.

One way to get around the wasted BVH would be to use set_faces() ourselves rather than using create_trimesh_shape().

Zylann commented 5 years ago

After optimizing: image

twice as fast now. It's all on Bullet :p

Zylann commented 5 years ago

Should be fixed by d4ed6372b743419f8bd7b018f720bc03f983d47f

TokisanGames commented 5 years ago

This is great, good work! Definitely feels snappier. However, it's crashing.

Blocky VT/heightmap is good.

Blocky VT/custom stream is good.

Everytime I try to run smooth VoxelTerrain/heightmap (your demo or mine):

Constructing VoxelDataLoader
View distance changed from 8 blocks to 16
Constructing VoxelMeshUpdater
ERROR: VectorWriteProxy<class Variant>::operator []: FATAL: Index p_index=0 out of size (((Vector<T> *)(this))->_cowdata.size()=0)
   At: C:\GD\godot\core/vector.h:49
CrashHandlerException: Program crashed
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[0] Array::operator[] (C:\GD\godot\core\array.cpp:75)
[1] Array::operator[] (C:\GD\godot\core\array.cpp:75)
[2] create_concave_polygon_shape (C:\GD\godot\modules\voxel\terrain\voxel_block.cpp:11)
[3] VoxelBlock::set_mesh (C:\GD\godot\modules\voxel\terrain\voxel_block.cpp:85)
[4] VoxelTerrain::_process (C:\GD\godot\modules\voxel\terrain\voxel_terrain.cpp:1060)
[5] VoxelTerrain::_notificationv (C:\GD\godot\modules\voxel\terrain\voxel_terrain.h:20)
[6] Object::notification (C:\GD\godot\core\object.cpp:933)
[7] SceneTree::_notify_group_pause (C:\GD\godot\scene\main\scene_tree.cpp:946)
[8] SceneTree::idle (C:\GD\godot\scene\main\scene_tree.cpp:515)
[9] Main::iteration (C:\GD\godot\main\main.cpp:1917)
[10] OS_Windows::run (C:\GD\godot\platform\windows\os_windows.cpp:3010)
[11] widechar_main (C:\GD\godot\platform\windows\godot_windows.cpp:162)
[12] _main (C:\GD\godot\platform\windows\godot_windows.cpp:186)
[13] main (C:\GD\godot\platform\windows\godot_windows.cpp:196)
[14] __scrt_common_main_seh (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
[15] BaseThreadInitThunk
-- END OF BACKTRACE --

VLT/noise almost always works fine. During the first session, it crashed after only the second or third creation. On my second session, I regenerated terrain probably 50 times and it didn't crash. On a third session with many terrain generations and hundreds of balls and collisions. It slowed my framerate, but wouldn't crash again.

Construct VoxelLodTerrain
Constructing VoxelDataLoader
Constructing VoxelMeshUpdater
Dropped 16 blocks from thread
Dropped 16 blocks from thread
Dropped 71 blocks from thread
Dropped 6 blocks from thread
Dropped 13 blocks from thread
Dropped 1 blocks from thread
Dropped 4 blocks from thread
Construct VoxelLodTerrain
Constructing VoxelDataLoader
Constructing VoxelMeshUpdater
Destroy VoxelLodTerrain
Destroying VoxelDataLoader
Destroying VoxelMeshUpdater
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
ERROR: Node not found: ../VoxelTerrain.
   At: scene\main\node.cpp:1369
Construct VoxelLodTerrain
Constructing VoxelDataLoader
Constructing VoxelMeshUpdater
Destroy VoxelLodTerrain
Destroying VoxelDataLoader
Destroying VoxelMeshUpdater
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
CrashHandlerException: Program crashed
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[0] BulletPhysicsDirectBodyState::get_contact_collider_id (C:\GD\godot\modules\bullet\rigid_body_bullet.cpp:178)
[1] BulletPhysicsDirectBodyState::get_contact_collider_id (C:\GD\godot\modules\bullet\rigid_body_bullet.cpp:178)
[2] RigidBody::_direct_state_changed (C:\GD\godot\scene\3d\physics_body.cpp:488)
[3] MethodBind1<RigidBody,Object *>::call (C:\GD\godot\core\method_bind.gen.inc:867)
[4] Object::call (C:\GD\godot\core\object.cpp:921)
[5] RigidBodyBullet::dispatch_callbacks (C:\GD\godot\modules\bullet\rigid_body_bullet.cpp:375)
[6] onBulletPreTickCallback (C:\GD\godot\modules\bullet\space_bullet.cpp:538)
[7] btDiscreteDynamicsWorld::internalSingleStepSimulation (C:\GD\godot\thirdparty\bullet\BulletDynamics\Dynamics\btDiscreteDynamicsWorld.cpp:462)
[8] btSoftRigidDynamicsWorld::internalSingleStepSimulation (C:\GD\godot\thirdparty\bullet\BulletSoftBody\btSoftRigidDynamicsWorld.cpp:91)
[9] btDiscreteDynamicsWorld::stepSimulation (C:\GD\godot\thirdparty\bullet\BulletDynamics\Dynamics\btDiscreteDynamicsWorld.cpp:435)
[10] BulletPhysicsServer::step (C:\GD\godot\modules\bullet\bullet_physics_server.cpp:1567)
[11] Main::iteration (C:\GD\godot\main\main.cpp:1903)
[12] OS_Windows::run (C:\GD\godot\platform\windows\os_windows.cpp:3010)
[13] widechar_main (C:\GD\godot\platform\windows\godot_windows.cpp:162)
[14] _main (C:\GD\godot\platform\windows\godot_windows.cpp:186)
[15] main (C:\GD\godot\platform\windows\godot_windows.cpp:196)
[16] __scrt_common_main_seh (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
[17] BaseThreadInitThunk
-- END OF BACKTRACE --
TokisanGames commented 5 years ago

Debugging:

_process: collidable_surface (set_mesh: surface_arrays) has _p/refcount/count=2, array size=0

_process: surface is not valid {_p=0x0000...fffffef { refcount={count=???} array={...}}} on either variable

image

TokisanGames commented 5 years ago

Adding a surface_arrays check to VoxelBlock:set_mesh (66) like this: if (mesh.is_valid() && !surface_arrays.empty()) {

prevents it from crashing, however, now the terrain won't load on either your demo or mine.

Zylann commented 5 years ago

Yeah sorry I think I omitted smooth surfaces in the VoxelTerrain one.

Zylann commented 5 years ago

You can try with 7a7f5f679bf9bba3c782f9af4a4689c90151f32f I don't know what the bullet crash is, maybe a bug that existed before?

TokisanGames commented 5 years ago

Smooth terrain looks good now, thank you.

Yeah I was just going to dismiss the physics crash, but it just happened again. And pushing it hard I was able to repeat it once more. It likely existed before, and is probably an engine bug. But I can't do it on command yet.

Also, while abusing the system, I was able to get a different type of crash from deleting and recreating the terrain while it was still loading. Though I've done this hundreds of times without issue, this one instance crashed:

Construct VoxelLodTerrain
Constructing VoxelDataLoader
Constructing VoxelMeshUpdater
Destroy VoxelLodTerrain
Destroying VoxelDataLoader
Destroying VoxelMeshUpdater
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Destroying VoxelMap
Dropped 16 blocks from thread
Dropped 24 blocks from thread
Received a block loading drop while we were still expecting it: lod0 (78, 10, 65)
Received a block loading drop while we were still expecting it: lod0 (78, 9, 65)
Received a block loading drop while we were still expecting it: lod0 (78, 10, 66)
Received a block loading drop while we were still expecting it: lod0 (78, 9, 66)
ERROR: VoxelBlockThreadManager<struct VoxelDataLoader::InputBlockData,struct VoxelDataLoader::OutputBlockData>::push_block_requests: FATAL: Condition ' *index < 0 || *index >= job.shared_input.blocks.size() ' is true.
   At: C:\GD\godot\modules\voxel\terrain\block_thread_manager.h:346
CrashHandlerException: Program crashed
Dumping the backtrace. Please include this when reporting the bug on https://github.com/godotengine/godot/issues
[0] VoxelBlockThreadManager<VoxelDataLoader::InputBlockData,VoxelDataLoader::OutputBlockData>::push_block_requests (C:\GD\godot\modules\voxel\terrain\block_thread_manager.h:346)
[1] VoxelBlockThreadManager<VoxelDataLoader::InputBlockData,VoxelDataLoader::OutputBlockData>::push_block_requests (C:\GD\godot\modules\voxel\terrain\block_thread_manager.h:346)
[2] VoxelBlockThreadManager<VoxelDataLoader::InputBlockData,VoxelDataLoader::OutputBlockData>::push (C:\GD\godot\modules\voxel\terrain\block_thread_manager.h:215)
[3] VoxelLodTerrain::_process (C:\GD\godot\modules\voxel\terrain\voxel_lod_terrain.cpp:703)
[4] VoxelLodTerrain::_notificationv (C:\GD\godot\modules\voxel\terrain\voxel_lod_terrain.h:19)
[5] Object::notification (C:\GD\godot\core\object.cpp:933)
[6] SceneTree::_notify_group_pause (C:\GD\godot\scene\main\scene_tree.cpp:946)
[7] SceneTree::idle (C:\GD\godot\scene\main\scene_tree.cpp:515)
[8] Main::iteration (C:\GD\godot\main\main.cpp:1917)
[9] OS_Windows::run (C:\GD\godot\platform\windows\os_windows.cpp:3010)
[10] widechar_main (C:\GD\godot\platform\windows\godot_windows.cpp:162)
[11] _main (C:\GD\godot\platform\windows\godot_windows.cpp:186)
[12] main (C:\GD\godot\platform\windows\godot_windows.cpp:196)
[13] __scrt_common_main_seh (d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288)
[14] BaseThreadInitThunk
-- END OF BACKTRACE --

Overall, I won't worry about it unless you see something obvious or until I can reproduce on command.

Zylann commented 5 years ago

Looks like I was right to put a bound check here, otherwise the STL didn't do it at all and could end up in heap corruption (which would make it even harder to identify).

If you get more crashes like this, can you please open a new issue? Like I did with https://github.com/Zylann/godot_voxel/issues/52, just listing stack traces to progressively narrow down the origin.

TokisanGames commented 5 years ago

Ok

Zylann commented 5 years ago

I think I found the cause of the crash db398a96e288ba168f7a48c315f0b9952cd13db8