#ifndef OPENMW_MWPHYSICS_MTPHYSICS_H #define OPENMW_MWPHYSICS_MTPHYSICS_H #include <atomic> #include <condition_variable> #include <optional> #include <shared_mutex> #include <thread> #include <unordered_set> #include <BulletCollision/CollisionDispatch/btCollisionWorld.h> #include <osg/Timer> #include "components/misc/budgetmeasurement.hpp" #include "physicssystem.hpp" #include "ptrholder.hpp" namespace Misc { class Barrier; } namespace MWRender { class DebugDrawer; } namespace MWPhysics { enum class LockingPolicy { NoLocks, ExclusiveLocksOnly, AllowSharedLocks, }; class PhysicsTaskScheduler { public: PhysicsTaskScheduler(float physicsDt, btCollisionWorld* collisionWorld, MWRender::DebugDrawer* debugDrawer); ~PhysicsTaskScheduler(); /// @brief move actors taking into account desired movements and collisions /// @param numSteps how much simulation step to run /// @param timeAccum accumulated time from previous run to interpolate movements /// @param actorsData per actor data needed to compute new positions /// @return new position of each actor void applyQueuedMovements(float& timeAccum, std::vector<Simulation>& simulations, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); void resetSimulation(const ActorMap& actors); // Thread safe wrappers void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, btCollisionWorld::RayResultCallback& resultCallback) const; void convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, btCollisionWorld::ConvexResultCallback& resultCallback) const; void contactTest(btCollisionObject* colObj, btCollisionWorld::ContactResultCallback& resultCallback); std::optional<btVector3> getHitPoint(const btTransform& from, btCollisionObject* target); void aabbTest(const btVector3& aabbMin, const btVector3& aabbMax, btBroadphaseAabbCallback& callback); void getAabb(const btCollisionObject* obj, btVector3& min, btVector3& max); void setCollisionFilterMask(btCollisionObject* collisionObject, int collisionFilterMask); void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup, int collisionFilterMask); void removeCollisionObject(btCollisionObject* collisionObject); void updateSingleAabb(const std::shared_ptr<PtrHolder>& ptr, bool immediate = false); bool getLineOfSight(const std::shared_ptr<Actor>& actor1, const std::shared_ptr<Actor>& actor2); void debugDraw(); void* getUserPointer(const btCollisionObject* object) const; void releaseSharedStates(); // destroy all objects whose destructor can't be safely called from // ~PhysicsTaskScheduler() private: class WorkersSync; void doSimulation(); void worker(); void updateActorsPositions(); bool hasLineOfSight(const Actor* actor1, const Actor* actor2); void refreshLOSCache(); void updateAabbs(); void updatePtrAabb(const std::shared_ptr<PtrHolder>& ptr); void updateStats(osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); std::tuple<int, float> calculateStepConfig(float timeAccum) const; void afterPreStep(); void afterPostStep(); void afterPostSim(); void syncWithMainThread(); void waitForWorkers(); void prepareWork(float& timeAccum, std::vector<Simulation>& simulations, osg::Timer_t frameStart, unsigned int frameNumber, osg::Stats& stats); std::unique_ptr<WorldFrameData> mWorldFrameData; std::vector<Simulation>* mSimulations = nullptr; std::unordered_set<const btCollisionObject*> mCollisionObjects; float mDefaultPhysicsDt; float mPhysicsDt; float mTimeAccum; btCollisionWorld* mCollisionWorld; MWRender::DebugDrawer* mDebugDrawer; std::vector<LOSRequest> mLOSCache; std::set<std::weak_ptr<PtrHolder>, std::owner_less<std::weak_ptr<PtrHolder>>> mUpdateAabb; // TODO: use std::experimental::flex_barrier or std::barrier once it becomes a thing std::unique_ptr<Misc::Barrier> mPreStepBarrier; std::unique_ptr<Misc::Barrier> mPostStepBarrier; std::unique_ptr<Misc::Barrier> mPostSimBarrier; LockingPolicy mLockingPolicy; unsigned mNumThreads; int mNumJobs; int mRemainingSteps; int mLOSCacheExpiry; bool mAdvanceSimulation; std::atomic<int> mNextJob; std::atomic<int> mNextLOS; std::vector<std::thread> mThreads; mutable std::shared_mutex mSimulationMutex; mutable std::shared_mutex mCollisionWorldMutex; mutable std::shared_mutex mLOSCacheMutex; mutable std::mutex mUpdateAabbMutex; unsigned int mFrameNumber; const osg::Timer* mTimer; int mPrevStepCount; Misc::BudgetMeasurement mBudget; Misc::BudgetMeasurement mAsyncBudget; unsigned int mBudgetCursor; osg::Timer_t mAsyncStartTime; osg::Timer_t mTimeBegin; osg::Timer_t mTimeEnd; osg::Timer_t mFrameStart; std::unique_ptr<WorkersSync> mWorkersSync; }; } #endif