diff --git a/engine/core/src/engine.zig b/engine/core/src/engine.zig index b6ce58e..a196955 100644 --- a/engine/core/src/engine.zig +++ b/engine/core/src/engine.zig @@ -5,6 +5,7 @@ const time = @import("engineTime.zig"); const core = @import("core.zig"); const jobs = @import("jobs.zig"); const math = @import("math.zig"); +const pscopes = core.algorithm.pscopes; const tracy = @import("tracy").t; const p2 = @import("p2"); @@ -84,6 +85,10 @@ pub const Engine = struct { delegates: EngineDelegates, + rootTimer: std.time.Timer, + scopesContext: *pscopes.ScopesContext, + timeTilCalibration: f64 = 1.0, // in seconds + calibrationPeriod: f64 = 1.0, // in seconds first: bool = true, pub fn init(allocator: std.mem.Allocator) !@This() { @@ -96,8 +101,10 @@ pub const Engine = struct { .lastEngineTime = 0.0, .jobManager = try JobManager.create(allocator), .eventors = .{}, + .rootTimer = try std.time.Timer.start(), .frameNumber = 1, .exitListeners = .{}, + .scopesContext = try pscopes.ScopesContext.create(allocator), // .nfdRuntime = try nfd.NFDRuntime.create(allocator, .{}), .delegates = EngineDelegates.init(allocator), }; @@ -113,6 +120,7 @@ pub const Engine = struct { self.jobManager.destroy(); self.engineObjectsByName.deinit(self.allocator); + self.scopesContext.destroy(); if (self.destroyListCore.items.len > 0) { var i: i32 = @intCast(self.destroyListCore.items.len - 1); @@ -239,14 +247,18 @@ pub const Engine = struct { var z1 = tracy.ZoneN(@src(), "time updates"); + var shouldCalibrate: bool = false; + if (self.first) { self.first = false; self.engineStartTime = newTime; self.lastEngineTime = newTime; self.sessionStamp = std.time.microTimestamp(); + shouldCalibrate = true; } if (newTime < self.lastEngineTime) { + try core.assertf(false, "negative deltaTime this should not be possible", .{}); std.debug.print("Warning! negative deltaTime? clamping to 0.0 newTime: {d} lastEngineTime:{d}", .{ newTime, self.lastEngineTime, @@ -254,6 +266,16 @@ pub const Engine = struct { } self.deltaTime = @max(newTime - self.lastEngineTime, 0.0); + + self.timeTilCalibration -= self.deltaTime; + + if (self.timeTilCalibration < 0 or shouldCalibrate) { + const zcalibrate = core.tracy.ZoneN(@src(), "calibrate timing scopeContext"); + self.scopesContext.calibrate(); + zcalibrate.End(); + self.timeTilCalibration = self.calibrationPeriod; + } + math.rollingAverage(&self.averageFrameTime, self.deltaTime, @floatFromInt(self.averageFrameSampleWindow)); z1.End(); diff --git a/engine/core/src/engineTime.zig b/engine/core/src/engineTime.zig index c2c3741..0238ecb 100644 --- a/engine/core/src/engineTime.zig +++ b/engine/core/src/engineTime.zig @@ -1,13 +1,15 @@ const std = @import("std"); const core = @import("core.zig"); +const pscopes = core.algorithm.pscopes; -// todo, replace with monotonic timer +// returns time since engine started in nanoseconds pub fn getEngineTime() f64 { - return @as(f64, @floatFromInt(std.time.milliTimestamp())) / 1000; + const read = core.getEngine().rootTimer.read(); + return @as(f64, @floatFromInt(read)) / std.time.ns_per_s; } -// return the current system timestamp in nanoseconds -pub fn getEngineTimeStamp() i128 { - return std.time.nanoTimestamp(); +// returns a pscopes.TimingScope for the current time. +pub fn takeProfilingStamp() pscopes.TimingScope { + return core.getEngine().scopesContext.stampScope(); } diff --git a/engine/core/src/scene.zig b/engine/core/src/scene.zig index e963d4a..ae45cc5 100644 --- a/engine/core/src/scene.zig +++ b/engine/core/src/scene.zig @@ -305,6 +305,8 @@ pub const SceneSystem = struct { cachedOutputs: std.ArrayList(std.ArrayList(core.Transform)) = .{}, writeOutList: std.ArrayList(std.ArrayList(usize)) = .{}, + lastUpdateTransformTime: f64 = 0.0, + pub const Field = SceneObjectSet.Field; pub const FieldType = SceneObjectSet.FieldType; @@ -354,7 +356,21 @@ pub const SceneSystem = struct { // then iterate over dynamicObjects array instead if (useParallelJob) { - core.parallelJob(UpdateWorldTransformsJob{}, false, 6) catch unreachable; + // we use 6 workers if the last timing scope ran > 2ms + // otherwise use 1 worker + var scope = core.engineTime.takeProfilingStamp(); + var workerCount: u32 = 1; + + if (self.lastUpdateTransformTime > 0.001) // 1ms + { + workerCount = 6; + } + + core.parallelJob(UpdateWorldTransformsJob{}, false, workerCount) catch unreachable; + scope.end(); + if (scope.duration()) |duration| { + self.lastUpdateTransformTime = duration; + } } else { for (Scene.SceneObjectContainer.denseItems(._repr), 0..) |*repr, i| { const settings = Scene.SceneObjectContainer.readDense(i, .settings); diff --git a/lib/p2/src/structures/pscopes.zig b/lib/p2/src/structures/pscopes.zig index daa500b..23f6206 100644 --- a/lib/p2/src/structures/pscopes.zig +++ b/lib/p2/src/structures/pscopes.zig @@ -88,7 +88,8 @@ pub const ScopesContext = struct { // calibrates the ticks per second divider. // spins for 1 ms then compares against the monotonic timer - // should be called once every second + // should be called once every second, this is kinda perscriptive, + // calling it more frequently will increase accuracy pub fn calibrate(self: *@This()) void { if (builtin.target.cpu.arch == .x86_64) { self.timer.reset(); diff --git a/lib/tracy/build.zig b/lib/tracy/build.zig index 7e80293..7a66f42 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; // } diff --git a/projects/sampleGame/externGame/externGame.zig b/projects/sampleGame/externGame/externGame.zig index 7b10160..2daf6b3 100644 --- a/projects/sampleGame/externGame/externGame.zig +++ b/projects/sampleGame/externGame/externGame.zig @@ -35,6 +35,8 @@ pub fn start_module(args: core.ModuleLoaderArgs) !void { core.engine_log("this change definitely happened :^) ", .{}); + core.engine_log("this change definitely happened :^) ", .{}); + _ = args; core.logDisplay("externGame", "accessing old object at {x} same object? {d}", .{ @intFromPtr(core.EngineObject(ExternGameObject).get()), @sizeOf(ExternGameObject) }); } @@ -549,6 +551,8 @@ pub const ExternGameObject = struct { core.MemoryTracker.dumpTimeline("timeline.txt") catch unreachable; } + ig.textf("parallel scene time {d:.4}ms", .{core.get(core.SceneSystem).lastUpdateTransformTime * 1000}); + if (ig.checkbox("vectors test", &self.displayVectorsTest)) {} if (ig.checkbox("showdebug", &ui.context().screenContext.drawDebug)) {}