refactor(physics): made physics single-threaded again

This commit is contained in:
maelstrom 2025-08-19 17:03:01 +02:00
parent 16f973c9c2
commit d922dd3727
6 changed files with 5 additions and 101 deletions

View file

@ -53,8 +53,6 @@ void BasePart::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWor
} }
void BasePart::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) { void BasePart::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
if (simulationTicket.has_value())
oldWorkspace->RemoveBody(shared<BasePart>());
} }
void BasePart::onUpdated(std::string property) { void BasePart::onUpdated(std::string property) {

View file

@ -34,12 +34,6 @@ struct PartConstructParams {
class Workspace; class Workspace;
#ifndef __SIMULATION_TICKET
#define __SIMULATION_TICKET
class BasePart;
typedef std::list<std::shared_ptr<BasePart>>::iterator SimulationTicket;
#endif
class DEF_INST_ABSTRACT_(explorer_icon="part") BasePart : public PVInstance { class DEF_INST_ABSTRACT_(explorer_icon="part") BasePart : public PVInstance {
AUTOGEN_PREAMBLE AUTOGEN_PREAMBLE
protected: protected:
@ -117,8 +111,6 @@ public:
DEF_SIGNAL SignalSource TouchEnded; DEF_SIGNAL SignalSource TouchEnded;
rp::RigidBody* rigidBody = nullptr; rp::RigidBody* rigidBody = nullptr;
std::optional<SimulationTicket> simulationTicket;
bool rigidBodyDirty = true;
inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); } inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); }
float GetSurfaceParamA(Vector3 face); float GetSurfaceParamA(Vector3 face);

View file

@ -157,12 +157,7 @@ void Workspace::ProcessContactEvents() {
} }
void Workspace::SyncPartPhysics(std::shared_ptr<BasePart> part) { void Workspace::SyncPartPhysics(std::shared_ptr<BasePart> part) {
if (globalPhysicsLock.try_lock()) { updatePartPhysics(part);
updatePartPhysics(part);
globalPhysicsLock.unlock();
} else {
part->rigidBodyDirty = true;
}
} }
tu_time_t physTime; tu_time_t physTime;
@ -172,28 +167,8 @@ void Workspace::PhysicsStep(float deltaTime) {
std::scoped_lock lock(globalPhysicsLock); std::scoped_lock lock(globalPhysicsLock);
physicsWorld->update(std::min(deltaTime / 2, (1/60.f))); physicsWorld->update(std::min(deltaTime / 2, (1/60.f)));
// Update queued objects
queueLock.lock();
for (QueueItem item : bodyQueue) {
if (item.action == QueueItem::QUEUEITEM_ADD) {
simulatedBodies.push_back(item.part);
item.part->simulationTicket = --simulatedBodies.end();
} else if (item.part->simulationTicket.has_value()) {
simulatedBodies.erase(item.part->simulationTicket.value());
item.part->simulationTicket = std::nullopt;
}
}
queueLock.unlock();
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance // TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (std::shared_ptr<BasePart> part : simulatedBodies) { for (std::shared_ptr<BasePart> part : simulatedBodies) {
// If the part's body is dirty, update it now instead
if (part->rigidBodyDirty) {
updatePartPhysics(part);
part->rigidBodyDirty = false;
continue;
}
if (!part->rigidBody) continue; if (!part->rigidBody) continue;
// Sync properties // Sync properties
@ -300,14 +275,10 @@ rp::Joint* Workspace::CreateJoint(const rp::JointInfo& jointInfo) {
} }
void Workspace::AddBody(std::shared_ptr<BasePart> part) { void Workspace::AddBody(std::shared_ptr<BasePart> part) {
queueLock.lock(); simulatedBodies.push_back(part);
bodyQueue.push_back({part, QueueItem::QUEUEITEM_ADD});
part->rigidBodyDirty = true;
queueLock.unlock();
} }
void Workspace::RemoveBody(std::shared_ptr<BasePart> part) { void Workspace::RemoveBody(std::shared_ptr<BasePart> part) {
queueLock.lock(); auto it = std::find(simulatedBodies.begin(), simulatedBodies.end(), part);
bodyQueue.push_back({part, QueueItem::QUEUEITEM_REMOVE}); simulatedBodies.erase(it);
queueLock.unlock();
} }

View file

@ -38,11 +38,6 @@ class Weld;
class Rotate; class Rotate;
class RotateV; class RotateV;
#ifndef __SIMULATION_TICKET
#define __SIMULATION_TICKET
typedef std::list<std::shared_ptr<BasePart>>::iterator SimulationTicket;
#endif
typedef std::function<FilterResult(std::shared_ptr<BasePart>)> RaycastFilter; typedef std::function<FilterResult(std::shared_ptr<BasePart>)> RaycastFilter;
struct QueueItem { struct QueueItem {
@ -79,7 +74,6 @@ class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service {
friend PhysicsEventListener; friend PhysicsEventListener;
std::list<std::shared_ptr<BasePart>> simulatedBodies; std::list<std::shared_ptr<BasePart>> simulatedBodies;
std::list<QueueItem> bodyQueue;
std::queue<ContactItem> contactQueue; std::queue<ContactItem> contactQueue;
std::mutex contactQueueLock; std::mutex contactQueueLock;
rp::PhysicsWorld* physicsWorld; rp::PhysicsWorld* physicsWorld;

View file

@ -23,36 +23,6 @@
#include "objects/service/selection.h" #include "objects/service/selection.h"
#include "timeutil.h" #include "timeutil.h"
class PlaceDocumentPhysicsWorker {
public:
std::mutex sync;
std::thread thread;
std::condition_variable runningCond;
bool running = false;
bool quit = false;
PlaceDocumentPhysicsWorker() : thread(&PlaceDocumentPhysicsWorker::doWork, this) {}
private:
tu_time_t lastTime = tu_clock_micros();
void doWork() {
do {
tu_time_t deltaTime = tu_clock_micros() - lastTime;
lastTime = tu_clock_micros();
// First frame is always empty
if (deltaTime > 100) {
gWorkspace()->PhysicsStep(float(deltaTime)/1'000'000);
}
std::this_thread::sleep_for(std::chrono::microseconds(16'667 - deltaTime));
std::unique_lock lock(sync);
runningCond.wait(lock, [&]{ return running || quit; });
lock.unlock();
} while (!quit);
}
};
PlaceDocument::PlaceDocument(QWidget* parent): PlaceDocument::PlaceDocument(QWidget* parent):
QMdiSubWindow(parent) { QMdiSubWindow(parent) {
placeWidget = new MainGLWidget; placeWidget = new MainGLWidget;
@ -62,29 +32,15 @@ PlaceDocument::PlaceDocument(QWidget* parent):
_runState = RUN_STOPPED; _runState = RUN_STOPPED;
updateSelectionListeners(gDataModel->GetService<Selection>()); updateSelectionListeners(gDataModel->GetService<Selection>());
worker = new PlaceDocumentPhysicsWorker();
} }
PlaceDocument::~PlaceDocument() { PlaceDocument::~PlaceDocument() {
worker->quit = true;
worker->runningCond.notify_all();
worker->thread.join();
}
void PlaceDocument::updatePhysicsWorker() {
{
std::lock_guard lock(worker->sync);
worker->running = _runState == RUN_RUNNING;
}
worker->runningCond.notify_all();
} }
void PlaceDocument::setRunState(RunState newState) { void PlaceDocument::setRunState(RunState newState) {
if (newState == RUN_RUNNING && _runState != RUN_RUNNING) { if (newState == RUN_RUNNING && _runState != RUN_RUNNING) {
if (_runState == RUN_PAUSED) { if (_runState == RUN_PAUSED) {
_runState = RUN_RUNNING; _runState = RUN_RUNNING;
updatePhysicsWorker();
return; return;
} }
@ -103,8 +59,6 @@ void PlaceDocument::setRunState(RunState newState) {
gDataModel = editModeDataModel; gDataModel = editModeDataModel;
updateSelectionListeners(gDataModel->GetService<Selection>()); updateSelectionListeners(gDataModel->GetService<Selection>());
} }
updatePhysicsWorker();
} }
void PlaceDocument::updateSelectionListeners(std::shared_ptr<Selection> selection) { void PlaceDocument::updateSelectionListeners(std::shared_ptr<Selection> selection) {
@ -130,7 +84,6 @@ void PlaceDocument::closeEvent(QCloseEvent *closeEvent) {
closeEvent->ignore(); closeEvent->ignore();
} }
std::shared_ptr<BasePart> shit;
void PlaceDocument::timerEvent(QTimerEvent* evt) { void PlaceDocument::timerEvent(QTimerEvent* evt) {
if (evt->timerId() != timer.timerId()) { if (evt->timerId() != timer.timerId()) {
QWidget::timerEvent(evt); QWidget::timerEvent(evt);
@ -140,6 +93,7 @@ void PlaceDocument::timerEvent(QTimerEvent* evt) {
placeWidget->repaint(); placeWidget->repaint();
placeWidget->updateCycle(); placeWidget->updateCycle();
gDataModel->GetService<ScriptContext>()->RunSleepingThreads(); gDataModel->GetService<ScriptContext>()->RunSleepingThreads();
if (_runState == RUN_RUNNING) gDataModel->GetService<Workspace>()->PhysicsStep(0.033);
gDataModel->GetService<Workspace>()->ProcessContactEvents(); gDataModel->GetService<Workspace>()->ProcessContactEvents();
} }

View file

@ -13,7 +13,6 @@
#include <type_traits> #include <type_traits>
class Selection; class Selection;
class PlaceDocumentPhysicsWorker;
enum RunState { enum RunState {
RUN_STOPPED, RUN_STOPPED,
@ -25,14 +24,10 @@ class PlaceDocument : public QMdiSubWindow {
QBasicTimer timer; QBasicTimer timer;
RunState _runState; RunState _runState;
PlaceDocumentPhysicsWorker* worker;
std::weak_ptr<SignalConnection> selectionConnection; std::weak_ptr<SignalConnection> selectionConnection;
void timerEvent(QTimerEvent*) override; void timerEvent(QTimerEvent*) override;
void updateSelectionListeners(std::shared_ptr<Selection>); void updateSelectionListeners(std::shared_ptr<Selection>);
void updatePhysicsWorker();
public: public:
MainGLWidget* placeWidget; MainGLWidget* placeWidget;
PlaceDocument(QWidget* parent = nullptr); PlaceDocument(QWidget* parent = nullptr);