diff options
| author | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-14 22:23:59 -0500 |
|---|---|---|
| committer | Nic Gaffney <gaffney_nic@protonmail.com> | 2025-10-14 22:23:59 -0500 |
| commit | dd3ca084640f794c59427a507686bbd9bec1ed6b (patch) | |
| tree | 4524af0c7a857a923387c07d58794de6d34a6b19 /src | |
| parent | ec2527a65c9609dc6098b55312a8286e30f9ba46 (diff) | |
| download | particle-sim-dd3ca084640f794c59427a507686bbd9bec1ed6b.tar.gz | |
Started work on emscripten, fixed single frame leak caused by infinite splits
Diffstat (limited to 'src')
| -rw-r--r-- | src/config.zig | 1 | ||||
| -rw-r--r-- | src/main.zig | 36 | ||||
| -rw-r--r-- | src/particle.zig | 11 | ||||
| -rw-r--r-- | src/quad.zig | 24 |
4 files changed, 56 insertions, 16 deletions
diff --git a/src/config.zig b/src/config.zig index 793fde7..3fea589 100644 --- a/src/config.zig +++ b/src/config.zig @@ -10,6 +10,7 @@ pub const colorAmnt = colors.len; pub const numThreads = 16; pub const minQuadSize = 1; pub const quadSplitLimit = 64; +pub var leafCapacityMod: u32 = 1; pub var particleCount: i32 = initialParticles; pub var minDistance: i32 = 20; pub var friction: f32 = 0.95; diff --git a/src/main.zig b/src/main.zig index 4d21dae..aa410a1 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); const rl = @import("raylib"); const z = @import("zgui"); +const builtin = @import("builtin"); const part = @import("particle.zig"); const cfg = @import("config.zig"); const img = @import("imgui.zig"); @@ -16,8 +17,28 @@ pub fn main() !void { cfg.colors = cfg.customColors(); cfg.rules = rules.ruleMatrix(true, true); rules.printRules(cfg.rules); + var gpa: std.heap.DebugAllocator(.{ + .safety = true, + .thread_safe = true, + }) = undefined; + defer { + if (builtin.mode == .Debug) { + _=gpa.detectLeaks(); + _=gpa.deinit(); + }} + + + const allocator = allocblk: { + if (builtin.mode == .Debug) { + gpa = std.heap.DebugAllocator(.{ .safety = true, .thread_safe = true, }){}; + break :allocblk gpa.allocator(); + } + if (builtin.target.os.tag == .emscripten) + break :allocblk std.heap.c_allocator + else + break :allocblk std.heap.smp_allocator; + }; - const allocator = std.heap.smp_allocator; // defer { // const leaked = smp.deinit(); // if (leaked == .leak) { @@ -58,6 +79,7 @@ pub fn main() !void { var particleArrs: [cfg.numThreads]std.ArrayList(part.particle) = undefined; for (0..cfg.numThreads) |i| particleArrs[i] = try std.ArrayList(part.particle).initCapacity(allocator, comptime @divFloor(cfg.particleMax, cfg.numThreads) + cfg.numThreads); + var particleArrEMCC = try std.ArrayList(part.particle).initCapacity(allocator, cfg.particleMax); defer allocator.free(pool); while (!rl.windowShouldClose()) { @@ -87,12 +109,16 @@ pub fn main() !void { } rl.clearBackground(rl.getColor(0x1E1E2EFF)); - for (pool, 0..) |*thread, i| { - thread.* = try std.Thread.spawn(.{}, part.updateVelocities, .{ particles, quadTree, i, &particleArrs[i] }); - } + if (builtin.target.os.tag != .emscripten) { + for (pool, 0..) |*thread, i| { + thread.* = try std.Thread.spawn(.{}, part.updateVelocities, .{ particles, quadTree, i, &particleArrs[i] }); + } - for (pool) |thread| + for (pool) |thread| thread.join(); + } else { + try part.updateVelocities(particles, quadTree, 0, &particleArrEMCC); + } quadTree.deinit(); diff --git a/src/particle.zig b/src/particle.zig index 14748dc..03de9ae 100644 --- a/src/particle.zig +++ b/src/particle.zig @@ -1,3 +1,4 @@ +const builtin = @import("builtin"); const cfg = @import("config.zig"); const std = @import("std"); const rl = @import("raylib"); @@ -24,12 +25,18 @@ pub fn initParticles(allocator: std.mem.Allocator, amnt: u32) !std.array_list.Ma pub fn updateVelocities( particles: std.array_list.Managed(particle), qtree: quad.Quad(particle, cfg.quadSplitLimit), - threadidx: u64, + threadidx: usize, particlesInRange: *std.ArrayList(particle), ) !void { const rules = cfg.rules; var i = threadidx; - while (i < particles.items.len) : (i += cfg.numThreads) { + while (i < particles.items.len) : (i += iterval: { + if (builtin.target.os.tag == .emscripten) + break :iterval 1 + else + break :iterval cfg.numThreads; + }) { + var p: *particle = &(particles.items[i]); defer particlesInRange.clearRetainingCapacity(); const radius = cfg.radius[p.colorId]; diff --git a/src/quad.zig b/src/quad.zig index db24589..c7177dc 100644 --- a/src/quad.zig +++ b/src/quad.zig @@ -26,7 +26,7 @@ pub fn Quad(T: type, comptime splitLimit: usize) type { pub fn init(allocator: std.mem.Allocator, tl: Point, br: Point) !Self { return Quad(T, splitLimit){ .allocator = allocator, - .nodes = try std.ArrayList(Node(T)).initCapacity(allocator, splitLimit), + .nodes = try std.ArrayList(Node(T)).initCapacity(allocator, splitLimit * cfg.leafCapacityMod), .topLeft = tl, .bottomRight = br, .children = [4]?*Quad(T, splitLimit){ null, null, null, null }, @@ -44,12 +44,12 @@ pub fn Quad(T: type, comptime splitLimit: usize) type { } fn shouldSplit(self: Self) bool { - if (@abs(self.topLeft.x - self.bottomRight.x) <= 8 and - @abs(self.topLeft.y - self.bottomRight.y) <= 8) { + if (@abs(self.topLeft.x - self.bottomRight.x) <= cfg.minQuadSize and + @abs(self.topLeft.y - self.bottomRight.y) <= cfg.minQuadSize) { return false; } if (self.nodes) |nodes| - return nodes.len >= cfg.quadSplitLimit; + return nodes.items.len >= cfg.quadSplitLimit; return false; } @@ -116,11 +116,17 @@ pub fn Quad(T: type, comptime splitLimit: usize) type { return; } if (self.nodes) |*nodes| { - nodes.appendBounded(node) catch { - try self.split(); - const quadrant = self.getQuadrant(node.pos); - try self.children[quadrant].?.insert(node); - }; + if (!shouldSplit(self.*)) { + nodes.appendBounded(node) catch { + cfg.leafCapacityMod += 1; + try nodes.ensureTotalCapacity(self.allocator, splitLimit * cfg.leafCapacityMod); + try nodes.appendBounded(node); + }; + return; + } + try self.split(); + const quadrant = self.getQuadrant(node.pos); + try self.children[quadrant].?.insert(node); } } |
