wip: wait until data is ready and has been consumed (couples physics and rendering thread again)

This commit is contained in:
2026-02-24 13:16:00 +01:00
parent 74065b7ea2
commit e7d4170b77
6 changed files with 37 additions and 22 deletions

View File

@ -2,6 +2,7 @@
#define __PHYSICS_HPP_ #define __PHYSICS_HPP_
#include <atomic> #include <atomic>
#include <condition_variable>
#include <mutex> #include <mutex>
#include <queue> #include <queue>
#include <raylib.h> #include <raylib.h>
@ -150,7 +151,11 @@ class ThreadedPhysics {
TracyLockable(std::mutex, command_mtx); TracyLockable(std::mutex, command_mtx);
std::queue<Command> pending_commands; std::queue<Command> pending_commands;
TracyLockable(std::mutex, pos_mtx); TracyLockable(std::mutex, data_mtx);
std::condition_variable_any data_ready_cnd;
std::condition_variable_any data_consumed_cnd;
bool data_ready = false;
bool data_consumed = true;
std::vector<Mass> masses; // Read by renderer std::vector<Mass> masses; // Read by renderer
std::unordered_map<State, int> state_masses; // Read by renderer std::unordered_map<State, int> state_masses; // Read by renderer
std::vector<Spring> springs; // Read by renderer std::vector<Spring> springs; // Read by renderer

View File

@ -61,10 +61,9 @@ public:
} }
private: private:
auto AllocateGraphInstancing(const std::vector<Mass> &masses) -> void; auto AllocateGraphInstancing(std::size_t size) -> void;
auto ReallocateGraphInstancingIfNecessary(const std::vector<Mass> &masses) auto ReallocateGraphInstancingIfNecessary(std::size_t size) -> void;
-> void;
public: public:
auto UpdateTextureSizes() -> void; auto UpdateTextureSizes() -> void;

View File

@ -81,12 +81,19 @@ auto main(int argc, char *argv[]) -> int {
// TODO: Don't download each frame if nothing changed, check for update // TODO: Don't download each frame if nothing changed, check for update
// first // first
{ {
std::lock_guard<LockableBase(std::mutex)> lock(physics.state.pos_mtx); std::unique_lock<LockableBase(std::mutex)> lock(physics.state.data_mtx);
physics.state.data_ready_cnd.wait(
lock, [&] { return physics.state.data_ready; });
masses = physics.state.masses; masses = physics.state.masses;
state_masses = physics.state.state_masses; state_masses = physics.state.state_masses;
springs = physics.state.springs; springs = physics.state.springs;
state_springs = physics.state.state_springs; state_springs = physics.state.state_springs;
physics.state.data_ready = false;
physics.state.data_consumed = true;
} }
physics.state.data_consumed_cnd.notify_one();
// Update the camera after the physics, so target lock is smooth // Update the camera after the physics, so target lock is smooth
if (state_masses.contains(state.current_state)) { if (state_masses.contains(state.current_state)) {

View File

@ -349,7 +349,7 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
[&](const struct ClearGraph &cg) { mass_springs.Clear(); }, [&](const struct ClearGraph &cg) { mass_springs.Clear(); },
}; };
while (state.running) { while (state.running.load()) {
// Handle queued commands // Handle queued commands
{ {
std::lock_guard<LockableBase(std::mutex)> lock(state.command_mtx); std::lock_guard<LockableBase(std::mutex)> lock(state.command_mtx);
@ -366,7 +366,6 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
} }
// Physics update // Physics update
// TODO: I need this thread to run at a constant rate
mass_springs.ClearForces(); mass_springs.ClearForces();
mass_springs.CalculateSpringForces(); mass_springs.CalculateSpringForces();
mass_springs.CalculateRepulsionForces(); mass_springs.CalculateRepulsionForces();
@ -380,12 +379,20 @@ auto ThreadedPhysics::PhysicsThread(ThreadedPhysics::PhysicsState &state)
// Publish the positions for the renderer (copy) // Publish the positions for the renderer (copy)
{ {
std::lock_guard<LockableBase(std::mutex)> lock(state.pos_mtx); std::unique_lock<LockableBase(std::mutex)> lock(state.data_mtx);
state.data_consumed_cnd.wait(lock, [&] { return state.data_consumed; });
state.masses = mass_springs.masses; state.masses = mass_springs.masses;
state.state_masses = mass_springs.state_masses; state.state_masses = mass_springs.state_masses;
state.springs = mass_springs.springs; state.springs = mass_springs.springs;
state.state_springs = mass_springs.state_springs; state.state_springs = mass_springs.state_springs;
state.data_ready = true;
state.data_consumed = false;
} }
// Notify the rendering thread that new data is available
state.data_ready_cnd.notify_one();
} }
} }

View File

@ -33,8 +33,7 @@ auto Renderer::UpdateTextureSizes() -> void {
menu_target = LoadRenderTexture(width * 2, MENU_HEIGHT); menu_target = LoadRenderTexture(width * 2, MENU_HEIGHT);
} }
auto Renderer::AllocateGraphInstancing(const std::vector<Mass> &masses) auto Renderer::AllocateGraphInstancing(std::size_t size) -> void {
-> void {
cube_instance = GenMeshCube(VERTEX_SIZE, VERTEX_SIZE, VERTEX_SIZE); cube_instance = GenMeshCube(VERTEX_SIZE, VERTEX_SIZE, VERTEX_SIZE);
instancing_shader = LoadShader("shader/instancing_vertex.glsl", instancing_shader = LoadShader("shader/instancing_vertex.glsl",
@ -48,16 +47,14 @@ auto Renderer::AllocateGraphInstancing(const std::vector<Mass> &masses)
vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR; vertex_mat.maps[MATERIAL_MAP_DIFFUSE].color = VERTEX_COLOR;
vertex_mat.shader = instancing_shader; vertex_mat.shader = instancing_shader;
transforms = (Matrix *)MemAlloc(masses.size() * sizeof(Matrix)); transforms = (Matrix *)MemAlloc(size * sizeof(Matrix));
transforms_size = masses.size(); transforms_size = size;
} }
auto Renderer::ReallocateGraphInstancingIfNecessary( auto Renderer::ReallocateGraphInstancingIfNecessary(std::size_t size) -> void {
const std::vector<Mass> &masses) -> void { if (transforms_size != size) {
if (transforms_size != masses.size()) { transforms = (Matrix *)MemRealloc(transforms, size * sizeof(Matrix));
transforms = transforms_size = size;
(Matrix *)MemRealloc(transforms, masses.size() * sizeof(Matrix));
transforms_size = masses.size();
} }
} }
@ -76,9 +73,9 @@ auto Renderer::DrawMassSprings(
ZoneNamedN(prepare_masses, "PrepareMasses", true); ZoneNamedN(prepare_masses, "PrepareMasses", true);
if (masses.size() < DRAW_VERTICES_LIMIT) { if (masses.size() < DRAW_VERTICES_LIMIT) {
if (transforms == nullptr) { if (transforms == nullptr) {
AllocateGraphInstancing(masses); AllocateGraphInstancing(masses.size());
} }
ReallocateGraphInstancingIfNecessary(masses); ReallocateGraphInstancingIfNecessary(masses.size());
int i = 0; int i = 0;
for (const auto &mass : masses) { for (const auto &mass : masses) {

View File

@ -43,7 +43,7 @@ auto StateManager::FillGraph() -> void {
// TODO: We have only dispatched the commands, the states won't be downloaded // TODO: We have only dispatched the commands, the states won't be downloaded
// when calling this... Make FindWinningStates() another command? // when calling this... Make FindWinningStates() another command?
// Or recalculate whenever masses.size() changes? // Or recalculate whenever masses.size() changes?
FindWinningStates(); // FindWinningStates();
} }
auto StateManager::UpdateGraph() -> void { auto StateManager::UpdateGraph() -> void {
@ -74,7 +74,7 @@ auto StateManager::FindWinningStates() -> void {
winning_states.clear(); winning_states.clear();
std::unordered_map<State, int> state_masses; std::unordered_map<State, int> state_masses;
{ {
std::lock_guard<LockableBase(std::mutex)> lock(physics.state.pos_mtx); std::lock_guard<LockableBase(std::mutex)> lock(physics.state.data_mtx);
state_masses = physics.state.state_masses; state_masses = physics.state.state_masses;
} }
for (const auto &[state, mass] : state_masses) { for (const auto &[state, mass] : state_masses) {