Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Do not merge] Experimental multithreaded physics #2070

Closed
wants to merge 1 commit into from

Conversation

akortunov
Copy link
Collaborator

@akortunov akortunov commented Dec 9, 2018

Do not merge, just a proof of concept!
Requires a Bullet, configured with a -DBULLET2_USE_THREAD_LOCKS=ON.
Indeed improves performance a lot on physics-bounded setups.

Unfortunately, it seems the convexSweepTest() is not fully threadsafe - I get crashes somewhere inside the Bullet code (usually here or here) during intensive collisions (e.g. after spawn of many actors in the same position).

Also sometimes I get the

System BUS error (signal 7)
Address: (nil)

error without log.

Any help to track down these issues would be welcome.

@lysol90
Copy link
Contributor

lysol90 commented Dec 9, 2018

So awesome to see this being worked on, even though just for testing purposes so far.

@Capostrophic
Copy link
Collaborator

points finger in air Is this the panacea.

@rhtucker
Copy link
Contributor

I tested between the 0.45 RC and this artifact. I didn't measure any differences, but as I said on Discord, I've never been physics limited. I have a 4th gen i7 in Windows 10 for reference.

Are there any settings I need to enable?

Excited to see what comes out of this, even if it's not directly helping me!

@Capostrophic
Copy link
Collaborator

You need to increase the number of threads in the config. Also, it's likely the artifact won't work properly because its dependency may have not built with the cmake option toggled on.

@psi29a
Copy link
Member

psi29a commented Dec 10, 2018

Unfortunately, it seems the convexSweepTest() is not fully threadsafe - I get crashes somewhere inside the Bullet code (usually here or here) during intensive collisions (e.g. after spawn of many actors in the same position).

Also sometimes I get the

System BUS error (signal 7)
Address: (nil)

error without log.

Does upstream Bullet know about these issues? They be able to help?

In addition to this, are we sharing state or passing by reference what should be passed by value to the threads?

@terabyte25
Copy link
Contributor

terabyte25 commented Dec 11, 2018

Seems good to me, I'm not getting any errors.

@lysol90
Copy link
Contributor

lysol90 commented Dec 11, 2018

@psi29a I suppose you were actually trying to quote terabyte25 and not edit his post..? :)

@psi29a
Copy link
Member

psi29a commented Dec 11, 2018

Seems good to me, I'm not getting any errors.

Did you set the value here:
https://github.com/OpenMW/openmw/pull/2070/files#diff-825b2622d77ecabde37681d85e337a93R247
to anything greater than 1?

What value did you chose?

If you just tested the branch as-is, you won't get any errors since it will remain in one thread.

@lysol90 that was my intent, yes, thanks ;)

@terabyte25
Copy link
Contributor

terabyte25 commented Dec 13, 2018

@psi29a I used 2 threads, then spawned ~150 vivecs.

I made sure to configure Bullet3 with the multithreaded option as well.

@xyzz
Copy link
Contributor

xyzz commented Dec 26, 2018

I've run it with tsan and there are definitely a ton of races, here's one example:

WARNING: ThreadSanitizer: data race (pid=32150)
  Read of size 4 at 0x7b1400ac5684 by thread T27:
    #0 operator-(btVector3 const&, btVector3 const&) /home/xyz/openmw-prefix/include/bullet/LinearMath/btVector3.h:810 (openmw+0x000000e34bc9)
    #1 btDbvt::rayTestInternal(btDbvtNode const*, btVector3 const&, btVector3 const&, btVector3 const&, unsigned int*, float, btVector3 const&, btVector3 const&, btAlignedObjectArray<btDbvtNode const*>&, btDbvt::ICollide&) const /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.h:1032 (libBulletCollision.so.2.87+0x00000009ad67)
    #2 btDbvtBroadphase::rayTest(btVector3 const&, btVector3 const&, btBroadphaseRayCallback&, btVector3 const&, btVector3 const&) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp:253 (libBulletCollision.so.2.87+0x000000096f40)
    #3 btCollisionWorld::convexSweepTest(btConvexShape const*, btTransform const&, btTransform const&, btCollisionWorld::ConvexResultCallback&, float) const /home/xyz/bullet3-2.87/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp:1116 (libBulletCollision.so.2.87+0x0000000c3a41)
    #4 MWPhysics::ActorTracer::doTrace(btCollisionObject const*, osg::Vec3f const&, osg::Vec3f const&, btCollisionWorld const*) /home/xyz/openmw/apps/openmw/mwphysics/trace.cpp:70 (openmw+0x0000014e499c)
    #5 MWPhysics::MovementSolver::move(osg::Vec3f, MWWorld::Ptr const&, MWPhysics::Actor*, osg::Vec3f const&, float, bool, float, float, btCollisionWorld const*, std::map<MWWorld::Ptr, MWWorld::Ptr, std::less<MWWorld::Ptr>, std::allocator<std::pair<MWWorld::Ptr const, MWWorld::Ptr> > >&, std::mutex&) <null> (openmw+0x0000014ce16c)
    #6 MWPhysics::PhysicsSystem::solveTask(int, int, int) /home/xyz/openmw/apps/openmw/mwphysics/physicssystem.cpp:1365 (openmw+0x0000014c89d0)
    #7 void std::__invoke_impl<void, void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>(std::__invoke_memfun_deref, void (MWPhysics::PhysicsSystem::*&&)(int, int, int), MWPhysics::PhysicsSystem*&&, int&&, int&&, int&&) <null> (openmw+0x0000014d714b)
    #8 std::__invoke_result<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>::type std::__invoke<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>(void (MWPhysics::PhysicsSystem::*&&)(int, int, int), MWPhysics::PhysicsSystem*&&, int&&, int&&, int&&) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/invoke.h:95 (openmw+0x0000014d285c)
    #9 decltype (__invoke((_S_declval<0ul>)(), (_S_declval<1ul>)(), (_S_declval<2ul>)(), (_S_declval<3ul>)(), (_S_declval<4ul>)())) std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> >::_M_invoke<0ul, 1ul, 2ul, 3ul, 4ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul, 4ul>) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:234 (openmw+0x0000014e4394)
    #10 std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> >::operator()() /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:243 (openmw+0x0000014e42b8)
    #11 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> > >::_M_run() /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:186 (openmw+0x0000014e425e)
    #12 <null> <null> (libstdc++.so.6+0x0000000e0f1d)

  Previous write of size 4 at 0x7b1400ac5684 by thread T44 (mutexes: write M310039515722236952):
    #0 Merge(btDbvtAabbMm const&, btDbvtAabbMm const&, btDbvtAabbMm&) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.h:683 (libBulletCollision.so.2.87+0x000000093fe0)
    #1 createnode /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:125 (libBulletCollision.so.2.87+0x00000008fe08)
    #2 insertleaf /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:150 (libBulletCollision.so.2.87+0x00000008ff8c)
    #3 btDbvt::update(btDbvtNode*, btDbvtAabbMm&) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:557 (libBulletCollision.so.2.87+0x000000092011)
    #4 btDbvt::update(btDbvtNode*, btDbvtAabbMm&, btVector3 const&, float) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:566 (libBulletCollision.so.2.87+0x0000000920df)
    #5 btDbvtBroadphase::setAabb(btBroadphaseProxy*, btVector3 const&, btVector3 const&, btDispatcher*) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp:337 (libBulletCollision.so.2.87+0x0000000975c2)
    #6 btCollisionWorld::updateSingleAabb(btCollisionObject*) /home/xyz/bullet3-2.87/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp:172 (libBulletCollision.so.2.87+0x0000000bf40d)
    #7 MWPhysics::PhysicsSystem::solveTask(int, int, int) /home/xyz/openmw/apps/openmw/mwphysics/physicssystem.cpp:1374 (openmw+0x0000014c8aba)
    #8 void std::__invoke_impl<void, void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>(std::__invoke_memfun_deref, void (MWPhysics::PhysicsSystem::*&&)(int, int, int), MWPhysics::PhysicsSystem*&&, int&&, int&&, int&&) <null> (openmw+0x0000014d714b)
    #9 std::__invoke_result<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>::type std::__invoke<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int>(void (MWPhysics::PhysicsSystem::*&&)(int, int, int), MWPhysics::PhysicsSystem*&&, int&&, int&&, int&&) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/bits/invoke.h:95 (openmw+0x0000014d285c)
    #10 decltype (__invoke((_S_declval<0ul>)(), (_S_declval<1ul>)(), (_S_declval<2ul>)(), (_S_declval<3ul>)(), (_S_declval<4ul>)())) std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> >::_M_invoke<0ul, 1ul, 2ul, 3ul, 4ul>(std::_Index_tuple<0ul, 1ul, 2ul, 3ul, 4ul>) /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:234 (openmw+0x0000014e4394)
    #11 std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> >::operator()() /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:243 (openmw+0x0000014e42b8)
    #12 std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (MWPhysics::PhysicsSystem::*)(int, int, int), MWPhysics::PhysicsSystem*, int, int, int> > >::_M_run() /usr/lib/gcc/x86_64-pc-linux-gnu/7.3.0/include/g++-v7/thread:186 (openmw+0x0000014e425e)
    #13 <null> <null> (libstdc++.so.6+0x0000000e0f1d)

  Location is heap block of size 79 at 0x7b1400ac5670 allocated by main thread:
    #0 malloc <null> (libtsan.so.0+0x000000027e55)
    #1 btAllocDefault /home/xyz/bullet3-2.87/src/LinearMath/btAlignedAllocator.cpp:24 (libLinearMath.so.2.87+0x00000001187e)
    #2 btAlignedAllocDefault /home/xyz/bullet3-2.87/src/LinearMath/btAlignedAllocator.cpp:70 (libLinearMath.so.2.87+0x000000011904)
    #3 btAlignedAllocInternal(unsigned long, int) /home/xyz/bullet3-2.87/src/LinearMath/btAlignedAllocator.cpp:251 (libLinearMath.so.2.87+0x000000011b4d)
    #4 createnode /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:99 (libBulletCollision.so.2.87+0x00000008fc14)
    #5 createnode /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:124 (libBulletCollision.so.2.87+0x00000008fded)
    #6 insertleaf /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:150 (libBulletCollision.so.2.87+0x00000008ff8c)
    #7 btDbvt::insert(btDbvtAabbMm const&, void*) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvt.cpp:520 (libBulletCollision.so.2.87+0x000000091d9f)
    #8 btDbvtBroadphase::createProxy(btVector3 const&, btVector3 const&, int, void*, int, int, btDispatcher*) /home/xyz/bullet3-2.87/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp:184 (libBulletCollision.so.2.87+0x000000096a0f)
    #9 btCollisionWorld::addCollisionObject(btCollisionObject*, int, int) /home/xyz/bullet3-2.87/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp:131 (libBulletCollision.so.2.87+0x0000000bf0cb)
    #10 MWPhysics::Actor::addCollisionMask(int) /home/xyz/openmw/apps/openmw/mwphysics/actor.cpp:106 (openmw+0x0000014e69c2)
    #11 MWPhysics::Actor::Actor(MWWorld::Ptr const&, osg::ref_ptr<Resource::BulletShape const>, btCollisionWorld*) /home/xyz/openmw/apps/openmw/mwphysics/actor.cpp:81 (openmw+0x0000014e662c)
    #12 MWPhysics::PhysicsSystem::addActor(MWWorld::Ptr const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/apps/openmw/mwphysics/physicssystem.cpp:1207 (openmw+0x0000014c7689)
    #13 MWClass::Actor::insertObject(MWWorld::Ptr const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, MWPhysics::PhysicsSystem&) const /home/xyz/openmw/apps/openmw/mwclass/actor.cpp:33 (openmw+0x00000153b0b4)
    #14 addObject /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:106 (openmw+0x00000127dd1b)
    #15 operator() /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:781 (openmw+0x000001283c30)
    #16 insert<MWWorld::Scene::insertCell(MWWorld::CellStore&, bool, Loading::Listener*)::<lambda(const MWWorld::Ptr&)> > /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:252 (openmw+0x00000128650f)
    #17 MWWorld::Scene::insertCell(MWWorld::CellStore&, bool, Loading::Listener*) /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:781 (openmw+0x000001283d74)
    #18 MWWorld::Scene::loadCell(MWWorld::CellStore*, Loading::Listener*, bool) /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:436 (openmw+0x00000128028e)
    #19 MWWorld::Scene::changeCellGrid(int, int, bool) /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:600 (openmw+0x0000012818f2)
    #20 MWWorld::Scene::changeToExteriorCell(ESM::Position const&, bool, bool) /home/xyz/openmw/apps/openmw/mwworld/scene.cpp:758 (openmw+0x000001283a19)
    #21 MWWorld::World::changeToExteriorCell(ESM::Position const&, bool, bool) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1049 (openmw+0x00000121c063)
    #22 MWWorld::World::changeToCell(ESM::CellId const&, ESM::Position const&, bool, bool) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1059 (openmw+0x00000121c16c)
    #23 MWState::StateManager::loadGame(MWState::Character const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/apps/openmw/mwstate/statemanagerimp.cpp:525 (openmw+0x000001643428)
    #24 MWGui::SaveGameDialog::accept(bool) /home/xyz/openmw/apps/openmw/mwgui/savegamedialog.cpp:301 (openmw+0x0000010f1d97)
    #25 MWGui::SaveGameDialog::onSlotActivated(MyGUI::ListBox*, unsigned long) /home/xyz/openmw/apps/openmw/mwgui/savegamedialog.cpp:71 (openmw+0x0000010ef758)
    #26 MyGUI::delegates::CMethodDelegate2<MWGui::SaveGameDialog, MyGUI::ListBox*, unsigned long>::invoke(MyGUI::ListBox*, unsigned long) /usr/include/MYGUI/MyGUI_DelegateImplement.h:89 (openmw+0x0000010f58e4)
    #27 MyGUI::ListBox::notifyMouseDoubleClick(MyGUI::Widget*) <null> (libMyGUIEngine.so.3+0x0000000f686b)
    #28 SDLUtil::InputWrapper::capture(bool) /home/xyz/openmw/components/sdlutil/sdlinputwrapper.cpp:85 (openmw+0x0000019412ea)
    #29 MWInput::InputManager::update(float, bool, bool) /home/xyz/openmw/apps/openmw/mwinput/inputmanagerimp.cpp:419 (openmw+0x000000e90e9a)
    #30 OMW::Engine::frame(float) /home/xyz/openmw/apps/openmw/engine.cpp:93 (openmw+0x00000166e09c)
    #31 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:716 (openmw+0x000001674d58)
    #32 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x000001653282)
    #33 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:97 (openmw+0x000001873759)
    #34 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000165337b)

  Mutex M310039515722236952 is already destroyed.

  Thread T27 (tid=32421, running) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x000000028e53)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0x0000000e1244)
    #2 MWPhysics::PhysicsSystem::applyQueuedMovement(float) /home/xyz/openmw/apps/openmw/mwphysics/physicssystem.cpp:1298 (openmw+0x0000014c809d)
    #3 MWWorld::World::doPhysics(float) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1537 (openmw+0x000001220508)
    #4 MWWorld::World::update(float, bool) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1782 (openmw+0x000001222011)
    #5 OMW::Engine::frame(float) /home/xyz/openmw/apps/openmw/engine.cpp:162 (openmw+0x00000166e6d4)
    #6 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:716 (openmw+0x000001674d58)
    #7 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x000001653282)
    #8 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:97 (openmw+0x000001873759)
    #9 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000165337b)

  Thread T44 (tid=32423, finished) created by main thread at:
    #0 pthread_create <null> (libtsan.so.0+0x000000028e53)
    #1 std::thread::_M_start_thread(std::unique_ptr<std::thread::_State, std::default_delete<std::thread::_State> >, void (*)()) <null> (libstdc++.so.6+0x0000000e1244)
    #2 MWPhysics::PhysicsSystem::applyQueuedMovement(float) /home/xyz/openmw/apps/openmw/mwphysics/physicssystem.cpp:1298 (openmw+0x0000014c809d)
    #3 MWWorld::World::doPhysics(float) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1537 (openmw+0x000001220508)
    #4 MWWorld::World::update(float, bool) /home/xyz/openmw/apps/openmw/mwworld/worldimp.cpp:1782 (openmw+0x000001222011)
    #5 OMW::Engine::frame(float) /home/xyz/openmw/apps/openmw/engine.cpp:162 (openmw+0x00000166e6d4)
    #6 OMW::Engine::go() /home/xyz/openmw/apps/openmw/engine.cpp:716 (openmw+0x000001674d58)
    #7 runApplication(int, char**) /home/xyz/openmw/apps/openmw/main.cpp:252 (openmw+0x000001653282)
    #8 wrapApplication(int (*)(int, char**), int, char**, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) /home/xyz/openmw/components/debug/debugging.cpp:97 (openmw+0x000001873759)
    #9 main /home/xyz/openmw/apps/openmw/main.cpp:264 (openmw+0x00000165337b)

SUMMARY: ThreadSanitizer: data race /home/xyz/openmw-prefix/include/bullet/LinearMath/btVector3.h:810 in operator-(btVector3 const&, btVector3 const&)

I think the problem is that while you lock the global mutex when updating mCollisionWorld, in https://github.com/OpenMW/openmw/pull/2070/files#diff-2318e0517898da5f974fd390a55511c9R1374 the reads are running without any mutexes held which creates a race. So basically I think while it's ok to run multiple convexSweepTest in parallel, it's not supposed to support modifying the world AND running convexSweepTest at the same time - maybe this could be split into two steps, i.e. first calculate all new positions, then update the world?

Also, not sure why you're getting signal 7 (SIGBUS? maybe it's the same reason but you didn't build with aserts?), I'm however getting an abort at https://github.com/bulletphysics/bullet3/blob/8bc1c8e01b1b2b9284df08385da0e03241f4e6aa/src/LinearMath/btThreads.cpp#L234 which basically seems that every thread gets it own stack until it runs out (https://github.com/bulletphysics/bullet3/blob/6e4707df5fa1f9927109e89a7cd2a6d6a6ddd072/src/BulletCollision/BroadphaseCollision/btDbvtBroadphase.cpp#L242); and since we keep spawning threads and the counter never decrements, it runs out pretty fast. It seems they fixed the issue bulletphysics/bullet3@74223ce fairly recently, however I think bullet still expects you to pre-spawn threads and reuse them instead of spawning them every time applyQueuedMovement is called.

@wareya
Copy link
Contributor

wareya commented Dec 26, 2018

maybe this could be split into two steps, i.e. first calculate all new positions, then update the world?

That would be difficult, because actors can interact. That is, actors that can interact within a given frame must be simulated one after the other (so, on the same thread), not interacting with past versions of one another, or they'll end up inside one another. The way things are structured right now, interacting with the "new" version of an actor's state means that you have to update the collision world. You could do it if actors weren't part of the world.

@akortunov
Copy link
Collaborator Author

akortunov commented Dec 27, 2018

I wonder how TES3MP handles players in different cells.
If physics calculations are server-side here and it has several collision worlds, it should be much easier to use multithreading here.

@akortunov
Copy link
Collaborator Author

That would be difficult, because actors can interact. That is, actors that can interact within a given frame must be simulated one after the other (so, on the same thread)

Interesting, how other games handle collisions? For example, Mount & Blade can handle several hundreds of actors in area without issues or FPS drop, even if they collide each other. I doubt it just moves actors one by one as OpenMW does.

@psi29a
Copy link
Member

psi29a commented Dec 30, 2018

bulletphysics/bullet3#2004 (comment)
Additional updates

@akortunov
Copy link
Collaborator Author

Can not reproduce crashes with bullet 2.88, but it still would be nice to spawn threads only once somehow.

@@ -1310,12 +1353,10 @@ namespace MWPhysics
else if (heightDiff < 0)
stats.addToFallHeight(-heightDiff);

mMutex.lock();
mMovementResults.push_back(std::make_pair(iter->first, interpolated));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me that mMovementResults updated only in this line (apart from cleaning before calculations) and newer used in calculations, so each thread may fill its own vector and then merge them in main thread. Then protection by a mutex not to be necessary

standingCollisionTracker[ptr] = ptrHolder->getPtr();
mutex.unlock();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is always better to use lock_guard to guarantee that the mutex will be always released, even when it seems that no exceptions will exist. C++ has long-range action, changes in one part of the code can easily affect another

currentThread++;
}

for(auto&& thread : threads)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the managing thread will stand idle while other threads are engaged in work. It is possible to generate mNumThreads-1 of additional threads and do calculation as well in this thread


void PhysicsSystem::solveTask(int offset, int module, int numSteps)
{
int i = 0;
const MWWorld::Ptr player = MWMechanics::getPlayer();
const MWBase::World *world = MWBase::Environment::get().getWorld();
PtrVelocityList::iterator iter = mMovementQueue.begin();
for(;iter != mMovementQueue.end();++iter)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You protect mMovementResults with mutex, but not protect mMovementQueue though they have the same type, so are not thread-safe. Or this vector can not change during physics update? If so, I think it is better to explicit comment that here (or even better, as doxygen comment on mMovementQueue)

@akortunov
Copy link
Collaborator Author

Closing this PR since is was designed as a proof of concept and I have no skills to implement it properly.

@akortunov akortunov closed this May 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
9 participants