Increase the timer resolution for a more precise sleep on Windows#2476
Increase the timer resolution for a more precise sleep on Windows#2476MiniBaker007 wants to merge 6 commits intoPixelGuys:masterfrom
Conversation
|
Hey, sorry but I will give it low prio, because it's technically already solved, even if the solution is crude. I do appreciate the effort tho! |
| @@ -513,7 +514,8 @@ pub fn main() void { // MARK: main() | |||
| std.log.info("Starting game with version {s}", .{settings.version.version}); | |||
|
|
|||
| if(builtin.os.tag == .windows) { | |||
There was a problem hiding this comment.
Since you made a new file for this, I think all operating system nuances should be handled inside of it, and the calling code should be OS-agnostic.
I'd suggest to structure the file similarly to the file_monitor.zig
| std.log.info("Set system timer resolution: {d} -> {d}.", .{initialResolution, currentResolution}); | ||
| } | ||
|
|
||
| pub fn resetTimerResolution() void { |
| const start = main.timestamp(); | ||
| const targetTime = start.addDuration(sleepDuration); | ||
|
|
||
| // Update currentResolution because it can change |
There was a problem hiding this comment.
Wait what? Can windows just change this behind our back? Otherwise I'd remove this, since this is a value that we should only set once.
| _ = NtQueryTimerResolution(&minResolution, &maxResolution, ¤tResolution); | ||
|
|
||
| // Negative value for relative time | ||
| var delayInterval = -(@divFloor(@as(windows.LARGE_INTEGER, @intCast(sleepDuration.nanoseconds)), 100) - currentResolution); |
There was a problem hiding this comment.
| var delayInterval = -(@divFloor(@as(windows.LARGE_INTEGER, @intCast(sleepDuration.nanoseconds)), 100) - currentResolution); | |
| var delayInterval = currentResolution - @divFloor(@as(windows.LARGE_INTEGER, @intCast(sleepDuration.nanoseconds)), 100); |
| // Negative value for relative time | ||
| var delayInterval = -(@divFloor(@as(windows.LARGE_INTEGER, @intCast(sleepDuration.nanoseconds)), 100) - currentResolution); | ||
| if(delayInterval < 0) { | ||
| _ = NtDelayExecution(windows.FALSE, &delayInterval); |
There was a problem hiding this comment.
Is this different from what Io.sleep calls internally?
|
|
||
| while(main.timestamp().durationTo(targetTime).nanoseconds > 0) { | ||
| std.atomic.spinLoopHint(); | ||
| } |
There was a problem hiding this comment.
I wonder if we actually need this busy loop here. Do we need to be this precise? I think it won't be a big deal if the actual time varies by +/- 1 ms.
There was a problem hiding this comment.
I think it would be better to handle the oversleeping case by faking the deltaTime and making the sleep in the next frame shorter to account for the difference.
| } | ||
| }; | ||
|
|
||
| pub fn setPriorityClass(priority: PriorityClass) void { |
There was a problem hiding this comment.
I'm not sure if this is a good idea. In general I'd say we should play nicely with the OS, and setting the entire process priority could seriously harm other applications running (e.g. discord) since Cubyz uses a ton of threads. If at all, we should only prioritize the render thread.
| } | ||
| } | ||
|
|
||
| extern "ntdll" fn NtQueryTimerResolution(MinimumResolution: *windows.ULONG, MaximumResolution: *windows.ULONG, CurrentResolution: *windows.ULONG) callconv(.winapi) windows.NTSTATUS; |
There was a problem hiding this comment.
I'd prefer to just import the corresponding headers with a C import. This reduces the chance to make mistakes.
On windows:
At the start of the application, the timer resolution is set to the maximum supported value, usualy 0.5 or 1 millisecond. To get consistant timing, we undersleep based on the timer resolution and busy wait the rest.
I also made it set the game's process priority class to "Above normal" ,which helps sleeping be more consistant. This might also generally improve the game's performance a little, I'm not sure.
From my testing, this achieves a quite consistant framerate, but that can depend on a lot of variables that are difficult to control for. So, if anyone is able to test this, that would be helpful.
Also, I'm not convinced with some of the ways I named things and structured the code, but I decided to get others' opinions rather than to keep overthinking it.