// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. #pragma once #include "Types.h" #include "Engine/Core/Core.h" #include #include #include class MultiThreadStepper; class PhysicsStepper { public: PhysicsStepper() { } virtual ~PhysicsStepper() { } public: virtual bool advance(PxScene* scene, PxReal dt, void* scratchBlock, PxU32 scratchBlockSize) = 0; virtual void wait(PxScene* scene) = 0; virtual void substepStrategy(const PxReal stepSize, PxU32& substepCount, PxReal& substepSize) = 0; virtual void setSubStepper(const PxReal stepSize, const PxU32 maxSteps) { } virtual void renderDone() { } }; class StepperTask : public PxLightCpuTask { public: void setStepper(MultiThreadStepper* stepper) { mStepper = stepper; } MultiThreadStepper* getStepper() { return mStepper; } const MultiThreadStepper* getStepper() const { return mStepper; } public: // [PxLightCpuTask] const char* getName() const override { return "Stepper Task"; } void run() override; protected: MultiThreadStepper* mStepper = nullptr; }; /// /// Physics simulation task used by the stepper. /// /// class StepperTaskSimulate : public StepperTask { public: /// /// Initializes a new instance of the class. /// StepperTaskSimulate() { } public: // [StepperTask] void run() override; }; class MultiThreadStepper : public PhysicsStepper { public: MultiThreadStepper() : mFirstCompletionPending(false) , mScene(nullptr) , mSync(nullptr) , mCurrentSubStep(0) , mNbSubSteps(0) { mCompletion0.setStepper(this); mCompletion1.setStepper(this); mSimulateTask.setStepper(this); } ~MultiThreadStepper(); bool advance(PxScene* scene, PxReal dt, void* scratchBlock, PxU32 scratchBlockSize) override; virtual void substepDone(StepperTask* ownerTask); void renderDone() override; // if mNbSubSteps is 0 then the sync will never // be set so waiting would cause a deadlock void wait(PxScene* scene) override { if (mNbSubSteps && mSync) mSync->wait(); } virtual void shutdown(); virtual void reset() = 0; void substepStrategy(const PxReal stepSize, PxU32& substepCount, PxReal& substepSize) override = 0; virtual void simulate(PxBaseTask* ownerTask); PxReal getSubStepSize() const { return mSubStepSize; } protected: void substep(StepperTask& completionTask); // we need two completion tasks because when multistepping we can't submit completion0 from the // substepDone function which is running inside completion0 bool mFirstCompletionPending; StepperTaskSimulate mSimulateTask; StepperTask mCompletion0, mCompletion1; PxScene* mScene; shdfnd::Sync* mSync; PxU32 mCurrentSubStep; PxU32 mNbSubSteps; PxReal mSubStepSize; void* mScratchBlock; PxU32 mScratchBlockSize; }; // The way this should be called is: // bool stepped = advance(dt) // // ... reads from the scene graph for rendering // // if(stepped) renderDone() // // ... anything that doesn't need access to the the physics scene // // if(stepped) sFixedStepper.wait() // // Note that per-substep callbacks to the sample need to be issued out of here, // between fetchResults and simulate class FixedStepper : public MultiThreadStepper { protected: PxReal mAccumulator; PxReal mFixedSubStepSize; PxU32 mMaxSubSteps; public: /// /// Initializes a new instance of the class. /// FixedStepper() : MultiThreadStepper() , mAccumulator(0) , mFixedSubStepSize(0.013f) , mMaxSubSteps(1) { } public: /// /// Setups the specified step size and the maximum amount of them. /// /// Size of the step (in seconds). /// The maximum amount of subsetps. void Setup(const float stepSize, const uint32 maxSubsetps = 1) { mFixedSubStepSize = stepSize; mMaxSubSteps = maxSubsetps; } public: // [MultiThreadStepper] void substepStrategy(const PxReal stepSize, PxU32& substepCount, PxReal& substepSize) override; void reset() override { mAccumulator = 0.0f; } void setSubStepper(const PxReal stepSize, const PxU32 maxSteps) override { mFixedSubStepSize = stepSize; mMaxSubSteps = maxSteps; } };