diff --git a/build.zig b/build.zig index 951ff1f..e51d848 100644 --- a/build.zig +++ b/build.zig @@ -256,9 +256,9 @@ const DynamicDepList: []const struct { dep: []const u8, artifact: []const u8 } = .{ .dep = "sdl3", .artifact = "SDL3" }, .{ .dep = "spng", .artifact = "spng_c" }, .{ .dep = "lua", .artifact = "luac" }, - .{ .dep = "miniaudio", .artifact = "miniaudio_c" }, + // .{ .dep = "miniaudio", .artifact = "miniaudio_c" }, .{ .dep = "zphysics", .artifact = "joltc" }, - .{ .dep = "enet", .artifact = "enet_c" }, + // .{ .dep = "enet", .artifact = "enet_c" }, .{ .dep = "ozz", .artifact = "ozz_cpp" }, }; @@ -401,6 +401,7 @@ pub const Program = struct { for (self.gameModules.items) |*m| { if (std.mem.eql(u8, m.name, module)) { m.enabled = enable; + std.debug.print("enabling module {s} {any}\n", .{ module, enable }); return; } } diff --git a/engine/core/src/core.zig b/engine/core/src/core.zig index 8a84e80..63a3984 100644 --- a/engine/core/src/core.zig +++ b/engine/core/src/core.zig @@ -468,3 +468,36 @@ pub fn PatchOrCreateObject(comptime T: type, args: engine.NeonObjectParams) void logging.engine_err("Unable to create object", .{}); }; } + +pub const u32a = std.atomic.Value(u32); +pub const i32a = std.atomic.Value(u32); + +var gBarrierCount: u32a = u32a.init(0); + +pub fn setBarrierCount(count: u32) void { + gBarrierCount.store(count, .seq_cst); +} + +pub fn getBarrierCount() u32 { + return gBarrierCount.load(.acquire); +} + +pub fn jobCount() u32 { + return getEngine().jobManager.numWorkers; +} + +pub fn waitSeconds(wait: f64) void { + const start = getEngineTime(); + + while (getEngineTime() - start < wait) { + std.Thread.sleep(10_000_000); + } +} + +pub fn itof32(i: anytype) f32 { + return @as(f32, @floatFromInt(i)); +} + +pub fn itof64(i: anytype) f32 { + return @as(f32, @floatFromInt(i)); +} diff --git a/engine/core/src/engineObject.zig b/engine/core/src/engineObject.zig index c41a978..3185e6e 100644 --- a/engine/core/src/engineObject.zig +++ b/engine/core/src/engineObject.zig @@ -38,6 +38,7 @@ pub fn MakeTypeName(comptime TargetType: type) Name { pub const EngineDataEventError = error{ UnknownStatePanic, BadInit, + JobsAbortShutdown, UnknownError, OutOfMemory, }; diff --git a/engine/core/src/engineTime.zig b/engine/core/src/engineTime.zig index 18c37ab..c2c3741 100644 --- a/engine/core/src/engineTime.zig +++ b/engine/core/src/engineTime.zig @@ -1,6 +1,8 @@ const std = @import("std"); -// todo need a better timing system than this +const core = @import("core.zig"); + +// todo, replace with monotonic timer pub fn getEngineTime() f64 { return @as(f64, @floatFromInt(std.time.milliTimestamp())) / 1000; } diff --git a/engine/core/src/extern/externModule.zig b/engine/core/src/extern/externModule.zig index 7e94d58..44cf23e 100644 --- a/engine/core/src/extern/externModule.zig +++ b/engine/core/src/extern/externModule.zig @@ -68,8 +68,8 @@ pub const LoadedModule = struct { const stagedPath = self.stagedPaths.getLast(); var lib = try std.DynLib.open(stagedPath); var mod = ModuleInterface{}; - mod.startup = lib.lookup(@TypeOf(mod.startup), "startup") orelse return error.BadModule; - mod.shutdown = lib.lookup(@TypeOf(mod.shutdown), "shutdown") orelse return error.BadModule; + mod.startup = lib.lookup(@TypeOf(mod.startup), "startup_module") orelse return error.BadModule; + mod.shutdown = lib.lookup(@TypeOf(mod.shutdown), "shutdown_module") orelse return error.BadModule; try self.loaded.append(allocator, mod); } diff --git a/engine/core/src/gameObject.zig b/engine/core/src/gameObject.zig index 7cb53f9..3923d5c 100644 --- a/engine/core/src/gameObject.zig +++ b/engine/core/src/gameObject.zig @@ -88,12 +88,7 @@ pub const GameObjectSystem = struct { return self; } - pub fn spawnObject( - self: *@This(), - comptime T: type, - objectName: []const u8, - parameters: SpawnParameters, - ) !*T { + pub fn spawnObject(self: *@This(), comptime T: type, objectName: []const u8, parameters: SpawnParameters) !*T { var n = core.MakeName(objectName); return self.spawnObjectByName(T, &n, parameters); } diff --git a/engine/core/src/jobs.zig b/engine/core/src/jobs.zig index 5d4ed3e..9694823 100644 --- a/engine/core/src/jobs.zig +++ b/engine/core/src/jobs.zig @@ -16,6 +16,7 @@ pub const JobManager = struct { jobQueueConcurrent: core.ConcurrentQueueU(JobContext), workers: []*JobWorker, numCpus: usize, + numWorkers: u32 = 0, pub fn create(allocator: std.mem.Allocator) !*@This() { var self = try allocator.create(@This()); @@ -29,6 +30,7 @@ pub const JobManager = struct { }; self.workers = self.allocator.alloc(*JobWorker, @max(self.numCpus - 2, 4)) catch unreachable; + self.numWorkers = @intCast(self.workers.len); core.engine_log("allocating worker count : {d}", .{self.workers.len}); var i: usize = 0; diff --git a/engine/ui/src/sgpu/textRenderer.zig b/engine/ui/src/sgpu/textRenderer.zig index fb80544..0f54e35 100644 --- a/engine/ui/src/sgpu/textRenderer.zig +++ b/engine/ui/src/sgpu/textRenderer.zig @@ -129,8 +129,13 @@ pub const TextRenderer = struct { return meshBuffer; } + pub fn submitMeshBuffer(self: *@This(), meshBuffer: *TextMeshBuffer) void { + _ = self; + _ = meshBuffer; + } + pub fn updateMeshBuffer(self: *@This(), meshBuffer: *TextMeshBuffer, copyPass: *gpu.GPUCopyPass, text: papyrus.DrawCommand.PrimitiveText) !void { - var z = tracy.ZoneN(@src(), "meshBuffer updates"); + var z = tracy.ZoneN(@src(), "papyrus meshBuffer updates"); defer z.End(); const string = text.text.getRead(); if (string.len == 0) diff --git a/extras/doomplayer/src/impl.c b/extras/doomplayer/src/impl.c index b930e31..9292a2c 100644 --- a/extras/doomplayer/src/impl.c +++ b/extras/doomplayer/src/impl.c @@ -4,7 +4,7 @@ #define DOOM_IMPLEMENT_PRINT #define DOOM_IMPLEMENT_MALLOC #define DOOM_IMPLEMENT_FILE_IO -#define DOOM_IMPLEMENT_SOCKETS +// #define DOOM_IMPLEMENT_SOCKETS #define DOOM_IMPLEMENT_GETTIME #define DOOM_IMPLEMENT_GETENV diff --git a/lib/enet/build.zig b/lib/enet/build.zig index 0a8502b..0eadf60 100644 --- a/lib/enet/build.zig +++ b/lib/enet/build.zig @@ -4,9 +4,10 @@ pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false; + _ = static_build; // am i good to always have enet as static? - const enet = if (static_build and false) b.addStaticLibrary(.{ + const enet = if (true) b.addStaticLibrary(.{ .name = "enet_c", .target = target, .optimize = optimize, @@ -28,8 +29,8 @@ pub fn build(b: *std.Build) void { "packet.c", "peer.c", "protocol.c", - "unix.c", "win32.c", + "unix.c", }, .flags = &.{"-DHAS_OFFSETOF=1"}, }); @@ -41,6 +42,7 @@ pub fn build(b: *std.Build) void { .windows => { enet.linkSystemLibrary("ws2_32"); enet.linkSystemLibrary("winmm"); + // Use .def file to control exports and avoid CRT symbol conflicts }, .linux, .macos => { // Unix platforms - no additional libraries needed diff --git a/lib/miniaudio/build.zig b/lib/miniaudio/build.zig index c8773cf..fd967f9 100644 --- a/lib/miniaudio/build.zig +++ b/lib/miniaudio/build.zig @@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const static_build = b.option(bool, "static_build", "builds backlog dependencies for static linking") orelse false; - const miniaudio_c = if (static_build) b.addStaticLibrary(.{ + const miniaudio_c = if (static_build or true) b.addStaticLibrary(.{ .name = "miniaudio_c", .target = target, .optimize = optimize, @@ -18,7 +18,7 @@ pub fn build(b: *std.Build) void { }); b.installArtifact(miniaudio_c); - miniaudio_c.addCSourceFile(.{ .file = b.path("src/miniaudio.cpp"), .flags = &.{"-fno-sanitize=all"} }); + miniaudio_c.addCSourceFile(.{ .file = b.path("src/miniaudio.cpp"), .flags = &.{"-fno-sanitize=all"}, .language = .c }); miniaudio_c.addIncludePath(b.path("include")); const mod = b.addModule("miniaudio", .{ .target = target, .optimize = optimize, .root_source_file = b.path("src/miniaudio.zig") }); diff --git a/lib/tracy/build.zig b/lib/tracy/build.zig index 22925af..8ef8368 100644 --- a/lib/tracy/build.zig +++ b/lib/tracy/build.zig @@ -11,7 +11,7 @@ pub fn build(b: *std.Build) void { //const tracy_enabled = b.option(bool, "tracy", "Enables tracy integration") orelse false; - const tracy_enabled: bool = false; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false; + const tracy_enabled: bool = true; //if (b.graph.env_map.hash_map.get("WITH_TRACY") != null) true else false; // if (b.graph.env_map.hash_map.get("WITH_TRACY")) |with_tracy| { // tracy_enabled = with_tracy; // } @@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void { .file = b.path(tracy_path ++ "/TracyClient.cpp"), .flags = &[_][]const u8{ "-DTRACY_ENABLE", + "-DTRACY_NO_EXIT", // MinGW doesn't have all the newfangled windows features, // so we need to pretend to have an older windows version. // "-D_WIN32_WINNT=0x601", diff --git a/lib/tracy/tracy.zig b/lib/tracy/tracy.zig index 1f561d8..4290914 100644 --- a/lib/tracy/tracy.zig +++ b/lib/tracy/tracy.zig @@ -223,6 +223,7 @@ const tracy_stub = struct { const tracy_full = struct { const c = @cImport({ @cDefine("TRACY_ENABLE", ""); + @cDefine("TRACY_NO_EXIT", ""); @cInclude("TracyC.h"); }); diff --git a/projects/build.zig b/projects/build.zig index edae9e1..e08ebbb 100644 --- a/projects/build.zig +++ b/projects/build.zig @@ -60,6 +60,7 @@ pub fn build(b: *std.Build) void { toolbox.setModuleEnabled("imgui", true); toolbox.setModuleEnabled("audio", false); toolbox.setModuleEnabled("sys", true); + toolbox.setModuleEnabled("net", false); toolbox.addExtraModule("gameExtras"); const toolboxExe = toolbox.compileInstall(); @@ -76,9 +77,12 @@ pub fn build(b: *std.Build) void { .desc = "headless program, core-only", .root_source_file = b.path("headless/main.zig"), }); + std.debug.print("headless", .{}); headless.setModuleEnabled("assets", false); headless.setModuleEnabled("platform", false); + headless.setModuleEnabled("audio", false); + headless.setModuleEnabled("net", false); headless.setModuleEnabled("rend", false); _ = headless.compileInstall(); diff --git a/projects/headless/main.zig b/projects/headless/main.zig index 5322bc3..24b6ab7 100644 --- a/projects/headless/main.zig +++ b/projects/headless/main.zig @@ -1,5 +1,6 @@ allocator: std.mem.Allocator = undefined, +timeLeft: f64 = 5.0, pub var NeonObjectTable: core.EngineObjectVTable = core.EngineObjectVTable.from(@This(), "Headless"); pub fn create(allocator: std.mem.Allocator) !*@This() { @@ -11,20 +12,110 @@ pub fn create(allocator: std.mem.Allocator) !*@This() { } pub fn prepare(self: *@This()) !void { - _ = self; + try self.testThing(); } pub fn tick(self: *@This(), dt: f64) void { - _ = self; - _ = dt; + // std.time.sleep(1000_000); + self.timeLeft -= dt; - core.exitNow(); + if (self.timeLeft < 0) + core.exitNow(); + + const start = core.getEngineTime(); + + // wait 10 ms + while (core.getEngineTime() - start < 0.010) { + std.Thread.sleep(10_000_000); + } } pub fn deinit(self: *@This()) void { self.allocator.destroy(self); } +const Src = std.builtin.SourceLocation; +pub fn Barrier(src: Src) type { + return struct { + pub const Src = src; + + pub var count: u32 = 0; + pub var generation: u32 = 0; + pub var mutex: std.Thread.Mutex = .{}; + pub var cv: std.Thread.Condition = .{}; + + pub fn sync(threadId: u32) !void { + _ = threadId; + + const barrierCount = core.getBarrierCount(); + + mutex.lock(); + defer mutex.unlock(); + + count += 1; + + if (count == barrierCount) { + // 2 second timeout + count = 0; + generation += 1; + cv.broadcast(); + } else { + try cv.timedWait(&mutex, 1000 * 1000 * 1000 * 2); + } + } + }; +} + +const MultiJob = struct { + pub var WorkGroupData = WorkGroupData.from(@This()); + + threadId: u32 = 0, + threadName: []const u8, + + pub var cv: std.Thread.Condition = .{}; + pub var mutex: std.Thread.Mutex = .{}; + + pub fn func(ctx: @This(), job: *core.JobContext) void { + _ = job; + core.tracy.SetThreadName(@ptrCast(ctx.threadName.ptr)); + ctx.loop() catch {}; + + if (core.getEngine().isShuttingDown()) {} + } + + pub fn loop(ctx: @This()) !void { + while (!core.getEngine().isShuttingDown()) { + try Barrier(@src()).sync(ctx.threadId); + try ctx.tick(); + if (ctx.threadId == 0) { + const z = core.tracy.ZoneN(@src(), "Thread 0 sleep"); + defer z.End(); + core.waitSeconds(0.01); + } + } + } + + pub fn tick(ctx: @This()) !void { + const z = core.tracy.ZoneN(@src(), "MultiJob Tick"); + defer z.End(); + // std.time.sleep(100_000 * ctx.threadId); + + try Barrier(@src()).sync(ctx.threadId); + } +}; + +pub fn testThing(self: *@This()) !void { + _ = self; + const workerCount = 16; + + core.setBarrierCount(workerCount); + + for (0..workerCount) |i| { + const name = try std.fmt.allocPrintZ(std.heap.c_allocator, "MultiJob{d}", .{i}); + try core.dispatchJob(MultiJob{ .threadName = name, .threadId = @intCast(i) }); + } +} + pub fn main() anyerror!void { var spec = try backlog.getSpec("headless"); // try spec.put("useGPA", .{ .boolean = false }); diff --git a/projects/minimal/src/main.zig b/projects/minimal/src/main.zig index 5f1da02..0fd2012 100644 --- a/projects/minimal/src/main.zig +++ b/projects/minimal/src/main.zig @@ -11,7 +11,7 @@ pub fn init(allocator: std.mem.Allocator) !*@This() { } pub fn prepare(self: *@This()) !void { - _ = self; + try self.fuckNazis(); core.engine_log("program ready", .{}); } diff --git a/projects/sampleGame/externGame/externGame.zig b/projects/sampleGame/externGame/externGame.zig index 0802213..b73212d 100644 --- a/projects/sampleGame/externGame/externGame.zig +++ b/projects/sampleGame/externGame/externGame.zig @@ -1,4 +1,4 @@ -pub export fn startup(p_allocator: *anyopaque, p_a: ?*anyopaque) bool { +pub export fn startup_module(p_allocator: *anyopaque, p_a: ?*anyopaque) bool { const allocator = core.modulePreamble(p_allocator, p_a) catch return false; _ = allocator; imgui.setupFromModule(); @@ -145,6 +145,20 @@ pub const ExternGameObject = struct { pub fn _beginPlay(self: *@This()) !void { try self.setupInputs(); + + if (self.firstTick) { + self.loadMap2() catch unreachable; + self.firstTick = false; + } + + // change this into a parallel for + for (0..300) |i| { + _ = try core.get(core.GameObjectSystem).spawnObject(BoxObject, "Box", .{ + .posRot = .{ + .position = .{ .x = core.itof32((i % 10)) * 2, .y = 15.0, .z = core.itof32(@divFloor(i, 10) * 2) }, + }, + }); + } } pub fn onMapReload(ctx: ?*anyopaque, _: core.ActionEvent) void { @@ -239,31 +253,6 @@ pub const ExternGameObject = struct { core.engine_log("map loaded", .{}); } - pub fn fuckNazis(self: *@This()) !void { - const MultiJob = struct { - threadName: []const u8, - - pub fn func(ctx: @This(), job: *core.JobContext) void { - _ = job; - core.tracy.SetThreadName(self.threadName); - while (true) { - ctx.tick(); - } - } - - pub fn tick(ctx: @This()) void { - _ = ctx; - core.tracy.ZoneN(@src(), "MultiJob Tick"); - std.time.sleep(0.01); - } - }; - - for (0..12) |i| { - const name = try std.fmt.allocPrint(self.heap.c_allocator, "MultiJob{d}", .{i}); - try core.dispatchJob(MultiJob{ .threadName = name }); - } - } - pub fn create(allocator: std.mem.Allocator) !*@This() { const self = try Slack.create(allocator); self.* = .{ @@ -277,8 +266,6 @@ pub const ExternGameObject = struct { const args = try core.ParseArgs(ExternGameArgs); - try self.fuckNazis(); - if (args.asClient) { self.launchAsClient = args.clientTarget; self.clientIndex = args.clientIndex; @@ -504,11 +491,6 @@ pub const ExternGameObject = struct { self.drawVector2Test(); } - if (self.firstTick) { - self.loadMap2() catch unreachable; - self.firstTick = false; - } - if (self.recompiling) { if (ig.begin("recompiling", null, .{})) { ig.textf("recompiling...", .{}); @@ -708,7 +690,7 @@ pub const ExternGameObject = struct { } }; -pub export fn shutdown() void {} +pub export fn shutdown_module() void {} const std = @import("std"); const backlog = @import("backlog");