multi-threaded
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 20 Oct 2021 04:09:12 +0000 (23:09 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Wed, 20 Oct 2021 04:09:12 +0000 (23:09 -0500)
src/physics.onyx
src/quadtree.onyx
src/settings.onyx
src/sim.onyx
src/vecmath.onyx

index 855ae0c761e373c2d3275ac66f9ef96c62933cd6..b59c512c6badf4ca0efbad99b1e1b631641a79da 100644 (file)
@@ -53,7 +53,7 @@ body_accumulate_move :: (body: ^Body, qt_bodies: ^QuadTree(^Body), dt: f32) {
     force := V2f.{0,0};
 
     #persist #thread_local other_bodies: [..] ^Body;
-    if other_bodies.data == null do array.init(^other_bodies);
+    if other_bodies.capacity == 0 do array.init(^other_bodies, 128);
     array.clear(^other_bodies);
     qt_bodies->query(AABB.{ body.pos.x - 300, body.pos.y - 300, 600, 600 }, ^other_bodies);
 
index b49f001165b97c0d4007d4329395b24844283e2c..abc5b96be1447f9ea6921ec3b966a8df281020da 100644 (file)
@@ -1,5 +1,7 @@
 #private_file array :: package core.array
 
+use package core.intrinsics.onyx
+
 AABB :: struct {
     x, y : f32;
     w, h : f32;
@@ -38,7 +40,7 @@ QuadTree :: struct (T: type_expr) {
         sw = null;
 
         point_count = 0;
-        for i: POINTS_PER_NODE do points[i] = null;
+        for i: POINTS_PER_NODE do points[i] = __zero_value(T);
     }
 
     subdivide :: (use q: ^QuadTree($T), a: Allocator) {
index e3d3bd151a7ce3f11e2b0ec6c297cf66fb01f394..6f26131f5d2c75f5b896d3d6b6a5845aebf08b46 100644 (file)
@@ -48,7 +48,7 @@ generate_random_settings :: (settings: ^Sim_Settings) {
     settings.near_repulsive_force = 100;
     settings.universe_scale = 20;
 
-    settings.thread_count = 4;
+    settings.thread_count = 1;
 
     memory.alloc_slice(^settings.body_relations, num_body_types * num_body_types);
     for i: num_body_types {
index a450d00906c0fa8a9faec8c554549e7d7bb943c2..7c504f7906e79bdfbfc3dddaed9f87e368048fcc 100644 (file)
@@ -24,9 +24,25 @@ Sim_State :: struct {
     qt_pool_buffer    : [] QuadTree(^Body);
     qt_pool_allocator : alloc.pool.PoolAllocator(QuadTree(^Body));
     qt_body_allocator : Allocator;
+
+    thread_data : [] Thread_Data;
+    threads     : [] thread.Thread;
+}
+
+Thread_Data :: struct {
+    id : i32;
+    state: ^Sim_State;
 }
 
-#thread_local state: Sim_State;
+body_program        : gl.GLProgram;
+body_buffer         : gl.GLBuffer;
+circle_mesh         : gl.GLVertexArrayObject;
+thread_sync_barrier : sync.Barrier;
+frame_sync_sema     : sync.Semaphore;
+global_dt           := 0.016f;
+keys: Set(i32);
+
+#thread_local state: ^Sim_State;
 
 sim_state_init :: (state: ^Sim_State) {
     array.init(^state.bodies);
@@ -51,11 +67,20 @@ sim_state_init :: (state: ^Sim_State) {
     state.qt_body_allocator = alloc.pool.make_allocator(^state.qt_pool_allocator);
 
     state.qt_bodies->init(AABB.{ -2000, -2000, 4000, 4000 });
-}
 
-body_program : gl.GLProgram;
-body_buffer  : gl.GLBuffer;
-circle_mesh  : gl.GLVertexArrayObject;
+    keys->init();
+
+    sync.barrier_init(^thread_sync_barrier, global_settings.thread_count);
+    sync.semaphore_init(^frame_sync_sema, 0);
+
+    memory.alloc_slice(^state.threads, global_settings.thread_count);
+    memory.alloc_slice(^state.thread_data, global_settings.thread_count);
+
+    for i: global_settings.thread_count {
+        state.thread_data[i] = .{ i, state };
+        thread.spawn(^state.threads[i], ^state.thread_data[i], thread_processing);
+    }
+}
 
 create_circle_mesh :: () -> gl.GLVertexArrayObject {
     vao := gl.createVertexArray();
@@ -87,17 +112,16 @@ create_circle_mesh :: () -> gl.GLVertexArrayObject {
     return vao;
 }
 
+paused := false;
 handle_event :: (ev) => {
     switch ev.kind {
         case .KeyDown {
-            switch ev.keyboard.keycode {
-                case 38 do state.camera.offset.y -= 6;
-                case 40 do state.camera.offset.y += 6;
-                case 39 do state.camera.offset.x += 6;
-                case 37 do state.camera.offset.x -= 6;
-                case 65 do state.camera.scale /= 1.02;
-                case 81 do state.camera.scale *= 1.02;
-            }
+            keys->insert(ev.keyboard.keycode);
+        }
+
+        case .KeyUp {
+            if ev.keyboard.keycode == #char "p" do paused = !paused;
+            keys->remove(ev.keyboard.keycode);
         }
 
         case .Resize {
@@ -127,19 +151,44 @@ handle_event :: (ev) => {
 }
 
 update :: (dt: f32) {
-    state.qt_bodies->init(.{ -2000, -2000, 4000, 4000 });
-    state.qt_pool_allocator = alloc.pool.make(state.qt_pool_buffer);
+    global_dt = dt;
+
+    if keys->has(38) do state.camera.offset.y -= 6;
+    if keys->has(40) do state.camera.offset.y += 6;
+    if keys->has(39) do state.camera.offset.x += 6;
+    if keys->has(37) do state.camera.offset.x -= 6;
+    if keys->has(65) do state.camera.scale /= 1.02;
+    if keys->has(81) do state.camera.scale *= 1.02;
+}
 
-    for ^it: state.bodies {
-        state.qt_bodies->insert(it, state.qt_body_allocator);
-    }
+update_bodies_partial :: (index: i32) {
+    low  := index * (global_settings.body_count / global_settings.thread_count);
+    high := (index + 1) * (global_settings.body_count / global_settings.thread_count);
 
-    for i: state.bodies.count {
-        body_accumulate_move(^state.bodies[i], ^state.qt_bodies, dt);
-    }
+    for i: low .. high do body_accumulate_move(^state.bodies[i], ^state.qt_bodies, global_dt);
+    sync.barrier_wait(^thread_sync_barrier);
+    for i: low .. high do body_apply_move(^state.bodies[i], global_dt);
+}
+
+thread_processing :: (td: ^Thread_Data) {
+    thread_id := td.id;
+    state = td.state;
 
-    for i: state.bodies.count {
-        body_apply_move(^state.bodies[i], dt);
+    while true {
+        if thread_id == 0 {
+            state.qt_bodies->init(.{ -2000, -2000, 4000, 4000 });
+            state.qt_pool_allocator = alloc.pool.make(state.qt_pool_buffer);
+
+            for ^it: state.bodies {
+                state.qt_bodies->insert(it, state.qt_body_allocator);
+            }
+        }
+
+        sync.barrier_wait(^thread_sync_barrier);
+        update_bodies_partial(thread_id);
+        sync.barrier_wait(^thread_sync_barrier);
+
+        sync.semaphore_wait(^frame_sync_sema);
     }
 }
 
@@ -166,6 +215,8 @@ draw :: () {
     gl.bindVertexArray(circle_mesh);
     gl.drawElementsInstanced(gl.TRIANGLE_FAN, 12, gl.UNSIGNED_BYTE, 0, state.bodies.count);
     gl.bindVertexArray(-1);
+
+    sync.semaphore_post(^frame_sync_sema, 0 if paused else global_settings.thread_count);
 }
 
 main :: (args) => {
@@ -184,7 +235,8 @@ main :: (args) => {
     planet_colors := cast(^f32) global_settings.body_color.data;
     gl.uniform3fv(planet_colors_loc, .{ ~~planet_colors, global_settings.body_type_count });
 
-    sim_state_init(^state);
+    state = make(Sim_State);
+    sim_state_init(state);
 
     gl.bindVertexArray(circle_mesh);
     body_buffer = gl.createBuffer();
index 6b35887c125e861159e0bde06c6ee9217bba0215..e724ffeb196ea66b562348db2b7f0886e58c66bc 100644 (file)
@@ -7,21 +7,21 @@ V2f :: struct {
     x, y: f32;
 }
 
-#operator+ (a, b: V2f)      => V2f.{ a.x + b.x, a.y + b.y };
-#operator- (a, b: V2f)      => V2f.{ a.x - b.x, a.y - b.y };
-#operator* (a: V2f, s: f32) => V2f.{ a.x * s,   a.y * s   };
+#operator+ macro (a, b: V2f)      => V2f.{ a.x + b.x, a.y + b.y };
+#operator- macro (a, b: V2f)      => V2f.{ a.x - b.x, a.y - b.y };
+#operator* macro (a: V2f, s: f32) => V2f.{ a.x * s,   a.y * s   };
 
-#operator+= (a: ^V2f, b: V2f) {
+#operator+= macro (a: ^V2f, b: V2f) {
     a.x += b.x;
     a.y += b.y;
 }
 
-#operator-= (a: ^V2f, b: V2f) {
+#operator-= macro (a: ^V2f, b: V2f) {
     a.x -= b.x;
     a.y -= b.y;
 }
 
-#operator*= (a: ^V2f, s: f32) {
+#operator*= macro (a: ^V2f, s: f32) {
     a.x += s;
     a.y += s;
 }