1 module pixelperfectengine.system.timer;
2 /*
3  * Copyright (C) 2015-2021, by Laszlo Szeremi under the Boost license.
4  *
5  * Pixel Perfect Engine, system.timer module
6  */
7 import collections.sortedlist;
8 public import core.time;
9 
10 static CoarseTimer timer;
11 
12 static this() {
13 	timer = new CoarseTimer();
14 }
15 /**
16  * Implements a coarse timer, that checks the time periodically (e.g. on VSYNC), then calls the delegate if the time
17  * assigned to it has been lapsed.
18  * Is fast and can effectively test for multiple elements, but is inaccurate which can even fluctuate if tests are done
19  * on VSYNC intervals. This will make the duration longer in every case (up to 16.7ms on 60Hz displays), but this
20  * still should be accurate enough for many cases.
21  * Delegates must take no arguments and  nothrow to avoid issues from that.
22  */
23 public class CoarseTimer {
24 	alias TimerReceiver = nothrow void delegate();
25 	/**
26 	 * A timer entry.
27 	 */
28 	protected struct Entry {
29 		TimerReceiver	onLapse;	///The delegate to be called.
30 		MonoTime		when;		///When the delegate must be called, in system time.
31 		int opCmp(const Entry other) const @nogc @trusted pure nothrow {
32 			return when.opCmp(other.when);
33 		}
34 		bool opEquals(const Entry other) const @nogc @trusted pure nothrow {
35 			return when == other.when;
36 		}
37 		size_t toHash() const @nogc @safe pure nothrow {
38 			return cast(size_t)when.ticks;
39 		}
40 	}
41 	protected SortedList!Entry	timerList;		///The list of timer entries.
42 	protected Entry[]			timerRegs;		///Secondary timer list
43 	protected uint				status;			///1 if during testing, 0 otherwise
44 	///CTOR
45 	public this() @safe pure nothrow {
46 
47 	}
48 	/**
49 	 * Registers an entry for the timer.
50 	 * Delta sets the amount of time into the future.
51 	 */
52 	public void register(TimerReceiver dg, Duration delta) @safe nothrow {
53 		if (!status)
54 			timerList.put(Entry(dg, MonoTime.currTime + delta));
55 		else
56 			timerRegs ~= Entry(dg, MonoTime.currTime + delta);
57 	}
58 	/**
59 	 * Tests the entries.
60 	 * If enough time has passed, then those entries will be called and deleted.
61 	 */
62 	public void test() nothrow {
63 		status = 1;
64 		while (timerList.length) {
65 			if (MonoTime.currTime >= timerList[0].when) {
66 				timerList[0].onLapse();
67  				timerList.remove(0);
68 			} else {
69 				break;
70 			}
71 		}
72 		foreach (e ; timerRegs)
73 			timerList.put(e);
74 		timerRegs.length = 0;
75 		status = 0;
76 	}
77 }