From 11617104c5da082bfadbecb801da991a656aa561 Mon Sep 17 00:00:00 2001 From: Christoph Urlacher Date: Tue, 24 Feb 2026 18:15:14 +0100 Subject: [PATCH] wip: draw physics thread ups --- include/config.hpp | 24 +++++++++++------------- include/physics.hpp | 1 + include/renderer.hpp | 2 +- src/main.cpp | 5 ++++- src/physics.cpp | 36 +++++++++++++++++++++++++++++++----- src/renderer.cpp | 5 ++++- 6 files changed, 52 insertions(+), 21 deletions(-) diff --git a/include/config.hpp b/include/config.hpp index 69017ef..72b658f 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -29,19 +29,17 @@ constexpr float ROT_SPEED = 1.0; constexpr float CAMERA_SMOOTH_SPEED = 15.0; // Physics Engine -constexpr float SIM_SPEED = 1.0; // How large each update should be -constexpr float TIMESTEP = 1.0 / 90; // Do 90 physics updates per second -constexpr float MASS = 1.0; // Mass spring system -constexpr float SPRING_CONSTANT = 5.0; // Mass spring system -constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system -constexpr float REST_LENGTH = 2.0; // Mass spring system -constexpr float VERLET_DAMPENING = 0.05; // [0, 1] -constexpr float BH_FORCE = 2.0; // BH: [1.0, 3.0] -constexpr float THETA = 1.0; // Barnes-Hut [0.5, 1.0] -constexpr float SOFTENING = 0.01; // Barnes-Hut [0.01, 1.0] -constexpr float GRID_FORCE = 0.02; // Grid: [0.0, ~0.05] -constexpr float REPULSION_RANGE = 5.0 * REST_LENGTH; // Grid -constexpr int REPULSION_GRID_REFRESH = 5; // Grid rebuild freq +constexpr float TARGET_UPS = 90; // How often to update physics +constexpr float TIMESTEP = 1.0 / TARGET_UPS; // Update interval in seconds +constexpr float SIM_SPEED = 4.0; // How large each update should be +constexpr float MASS = 1.0; // Mass spring system +constexpr float SPRING_CONSTANT = 5.0; // Mass spring system +constexpr float DAMPENING_CONSTANT = 1.0; // Mass spring system +constexpr float REST_LENGTH = 2.0; // Mass spring system +constexpr float VERLET_DAMPENING = 0.05; // [0, 1] +constexpr float BH_FORCE = 2.0; // Barnes-Hut [1.0, 3.0] +constexpr float THETA = 0.9; // Barnes-Hut [0.5, 1.0] +constexpr float SOFTENING = 0.01; // Barnes-Hut [0.01, 1.0] // Graph Drawing constexpr float VERTEX_SIZE = 0.5; diff --git a/include/physics.hpp b/include/physics.hpp index 5bd4bcf..2dd8b36 100644 --- a/include/physics.hpp +++ b/include/physics.hpp @@ -122,6 +122,7 @@ class ThreadedPhysics { TracyLockable(std::mutex, data_mtx); std::condition_variable_any data_ready_cnd; std::condition_variable_any data_consumed_cnd; + unsigned int ups = 0; bool data_ready = false; bool data_consumed = true; std::vector masses; // Read by renderer diff --git a/include/renderer.hpp b/include/renderer.hpp index 85b44b2..64a67dc 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -77,7 +77,7 @@ public: const std::vector> &springs) -> void; - auto DrawTextures() -> void; + auto DrawTextures(float ups) -> void; }; #endif diff --git a/src/main.cpp b/src/main.cpp index c234f39..c98f9d5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -44,6 +44,7 @@ auto main(int argc, char *argv[]) -> int { OrbitCamera3D camera; Renderer renderer(camera, state, input); + unsigned int ups; std::vector masses; // Read from physics std::vector> springs; // Read from physics @@ -61,6 +62,8 @@ auto main(int argc, char *argv[]) -> int { { std::unique_lock lock(physics.state.data_mtx); + ups = physics.state.ups; + // Only copy data if any has been produced if (physics.state.data_ready) { masses = physics.state.masses; @@ -88,7 +91,7 @@ auto main(int argc, char *argv[]) -> int { renderer.DrawMassSprings(masses, springs); renderer.DrawKlotski(); renderer.DrawMenu(masses, springs); - renderer.DrawTextures(); + renderer.DrawTextures(ups); FrameMarkEnd("MainThread"); } diff --git a/src/physics.cpp b/src/physics.cpp index 6c18d32..fae51fd 100644 --- a/src/physics.cpp +++ b/src/physics.cpp @@ -5,8 +5,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -190,8 +192,21 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state) [&](const struct ClearGraph &cg) { mass_springs.Clear(); }, }; + std::chrono::time_point last = std::chrono::high_resolution_clock::now(); + std::chrono::duration accumulator(0); + std::chrono::duration update_accumulator(0); + unsigned int updates = 0; + while (state.running.load()) { FrameMarkStart("PhysicsThread"); + + // Time tracking + std::chrono::time_point now = std::chrono::high_resolution_clock::now(); + std::chrono::duration deltatime = now - last; + accumulator += deltatime; + update_accumulator += deltatime; + last = now; + // Handle queued commands { std::lock_guard lock(state.command_mtx); @@ -208,11 +223,15 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state) } // Physics update - // TODO: Respect thread-local deltatime + calculate UPS - mass_springs.ClearForces(); - mass_springs.CalculateSpringForces(); - mass_springs.CalculateRepulsionForces(); - mass_springs.VerletUpdate(TIMESTEP * SIM_SPEED); + if (accumulator.count() > TIMESTEP) { + mass_springs.ClearForces(); + mass_springs.CalculateSpringForces(); + mass_springs.CalculateRepulsionForces(); + mass_springs.VerletUpdate(TIMESTEP * SIM_SPEED); + + ++updates; + accumulator -= std::chrono::duration(TIMESTEP); + } // Publish the positions for the renderer (copy) FrameMarkStart("PhysicsThreadProduceLock"); @@ -225,6 +244,13 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state) break; } + if (update_accumulator.count() > 1.0) { + // Update each second + state.ups = updates; + updates = 0; + update_accumulator = std::chrono::duration(0); + } + state.masses.clear(); state.masses.reserve(mass_springs.masses.size()); for (const auto &mass : mass_springs.masses) { diff --git a/src/renderer.cpp b/src/renderer.cpp index 8bb9c42..b66b1d0 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -316,7 +316,7 @@ auto Renderer::DrawMenu( EndTextureMode(); } -auto Renderer::DrawTextures() -> void { +auto Renderer::DrawTextures(float ups) -> void { BeginDrawing(); DrawTextureRec(menu_target.texture, Rectangle(0, 0, menu_target.texture.width, @@ -330,6 +330,9 @@ auto Renderer::DrawTextures() -> void { Rectangle(0, 0, render_target.texture.width, -1 * render_target.texture.height), Vector2(GetScreenWidth() / 2.0, MENU_HEIGHT), WHITE); + DrawFPS(GetScreenWidth() / 2 + 10, MENU_HEIGHT + 10); + DrawText(TextFormat("%.0f UPS", ups), GetScreenWidth() / 2 + 120, + MENU_HEIGHT + 10, 20, ORANGE); EndDrawing(); }