Skip to content

Commit 6866ed3

Browse files
committed
feat(timer): implement high-resolution timer with RF2.1 microsecond precision
- Add TimePoint struct for tick-based time representation - Add Duration struct with conversion methods (ms/us/ns) - Implement Timer class with start/stop/reset/elapsed/tick methods - Implement ScopeTimer RAII wrapper for profiler integration - Add comprehensive TDD test suite (31 tests, all passing) - Support both running state tracking and accumulated time calculation - Use std::chrono::high_resolution_clock as foundation - Update CMakeLists to include Timer implementation and tests Tests verify: - TimePoint comparison and arithmetic - Duration conversion accuracy (microsecond precision) - Timer lifecycle (start/stop/reset cycles) - Tick accumulation for frame timing - ScopeTimer RAII semantics
1 parent aa75781 commit 6866ed3

5 files changed

Lines changed: 525 additions & 2 deletions

File tree

CMakeLists.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,17 @@ if(CMAKE_BUILD_TYPE STREQUAL "Debug")
1212
add_compile_definitions(CF_DEBUG)
1313
endif()
1414

15-
add_library(Caffeine INTERFACE)
16-
target_include_directories(Caffeine INTERFACE
15+
add_library(Caffeine
16+
src/core/Timer.cpp
17+
)
18+
19+
target_include_directories(Caffeine PUBLIC
1720
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
1821
$<INSTALL_INTERFACE:include>
1922
)
2023

24+
target_compile_features(Caffeine PUBLIC cxx_std_20)
25+
2126
add_library(Caffeine::Core ALIAS Caffeine)
2227

2328
add_subdirectory(tests)

src/core/Timer.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
#include "Timer.hpp"
2+
#include <chrono>
3+
4+
namespace Caffeine::Core {
5+
6+
static TimePoint getCurrentTimePoint() {
7+
auto now = std::chrono::high_resolution_clock::now();
8+
auto elapsed = now.time_since_epoch();
9+
u64 nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(elapsed).count();
10+
return TimePoint(nanos);
11+
}
12+
13+
Timer::Timer()
14+
: m_is_running(false), m_accumulated_ticks(0) {
15+
}
16+
17+
Timer::~Timer() {
18+
}
19+
20+
void Timer::start() {
21+
if (!m_is_running) {
22+
m_start_time = getCurrentTimePoint();
23+
m_is_running = true;
24+
}
25+
}
26+
27+
void Timer::stop() {
28+
if (m_is_running) {
29+
m_stop_time = getCurrentTimePoint();
30+
u64 ticks = m_stop_time.ticks - m_start_time.ticks;
31+
m_accumulated_ticks += ticks;
32+
m_is_running = false;
33+
}
34+
}
35+
36+
void Timer::reset() {
37+
m_accumulated_ticks = 0;
38+
m_start_time.ticks = 0;
39+
m_stop_time.ticks = 0;
40+
m_is_running = false;
41+
}
42+
43+
Duration Timer::elapsed() const {
44+
if (m_is_running) {
45+
TimePoint current = getCurrentTimePoint();
46+
u64 ticks = current.ticks - m_start_time.ticks + m_accumulated_ticks;
47+
return Duration::fromNanos(static_cast<double>(ticks));
48+
} else {
49+
return Duration::fromNanos(static_cast<double>(m_accumulated_ticks));
50+
}
51+
}
52+
53+
Duration Timer::tick() {
54+
if (m_is_running) {
55+
TimePoint current = getCurrentTimePoint();
56+
u64 ticks = current.ticks - m_start_time.ticks;
57+
m_start_time = current;
58+
return Duration::fromNanos(static_cast<double>(ticks));
59+
}
60+
return Duration::fromSeconds(0.0);
61+
}
62+
63+
bool Timer::isRunning() const {
64+
return m_is_running;
65+
}
66+
67+
ScopeTimer::ScopeTimer(const char* name)
68+
: m_name(name) {
69+
m_timer.start();
70+
}
71+
72+
ScopeTimer::~ScopeTimer() {
73+
m_timer.stop();
74+
}
75+
76+
}

src/core/Timer.hpp

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#pragma once
2+
3+
#include "../core/Types.hpp"
4+
5+
namespace Caffeine::Core {
6+
7+
struct TimePoint {
8+
u64 ticks = 0;
9+
10+
TimePoint() = default;
11+
explicit TimePoint(u64 t) : ticks(t) {}
12+
13+
bool operator<(const TimePoint& other) const {
14+
return ticks < other.ticks;
15+
}
16+
17+
bool operator<=(const TimePoint& other) const {
18+
return ticks <= other.ticks;
19+
}
20+
21+
bool operator>(const TimePoint& other) const {
22+
return ticks > other.ticks;
23+
}
24+
25+
bool operator>=(const TimePoint& other) const {
26+
return ticks >= other.ticks;
27+
}
28+
29+
bool operator==(const TimePoint& other) const {
30+
return ticks == other.ticks;
31+
}
32+
33+
bool operator!=(const TimePoint& other) const {
34+
return ticks != other.ticks;
35+
}
36+
};
37+
38+
struct Duration {
39+
f64 seconds = 0.0;
40+
41+
Duration() = default;
42+
explicit Duration(f64 s) : seconds(s) {}
43+
44+
static Duration fromSeconds(f64 s) {
45+
return Duration(s);
46+
}
47+
48+
static Duration fromMillis(f64 ms) {
49+
return Duration(ms / 1000.0);
50+
}
51+
52+
static Duration fromMicros(f64 us) {
53+
return Duration(us / 1000000.0);
54+
}
55+
56+
static Duration fromNanos(f64 ns) {
57+
return Duration(ns / 1000000000.0);
58+
}
59+
60+
f64 millis() const {
61+
return seconds * 1000.0;
62+
}
63+
64+
f64 micros() const {
65+
return seconds * 1000000.0;
66+
}
67+
68+
f64 nanos() const {
69+
return seconds * 1000000000.0;
70+
}
71+
72+
Duration operator+(const Duration& other) const {
73+
return Duration(seconds + other.seconds);
74+
}
75+
76+
Duration operator-(const Duration& other) const {
77+
return Duration(seconds - other.seconds);
78+
}
79+
80+
Duration operator*(f64 scalar) const {
81+
return Duration(seconds * scalar);
82+
}
83+
84+
friend Duration operator*(f64 scalar, const Duration& d) {
85+
return Duration(scalar * d.seconds);
86+
}
87+
88+
Duration operator/(f64 scalar) const {
89+
return Duration(seconds / scalar);
90+
}
91+
92+
bool operator<(const Duration& other) const {
93+
return seconds < other.seconds;
94+
}
95+
96+
bool operator<=(const Duration& other) const {
97+
return seconds <= other.seconds;
98+
}
99+
100+
bool operator>(const Duration& other) const {
101+
return seconds > other.seconds;
102+
}
103+
104+
bool operator>=(const Duration& other) const {
105+
return seconds >= other.seconds;
106+
}
107+
108+
bool operator==(const Duration& other) const {
109+
const f64 epsilon = 1e-15;
110+
return (seconds - other.seconds) < epsilon && (seconds - other.seconds) > -epsilon;
111+
}
112+
113+
bool operator!=(const Duration& other) const {
114+
return !(*this == other);
115+
}
116+
};
117+
118+
inline Duration operator-(const TimePoint& a, const TimePoint& b) {
119+
u64 tick_diff = (a.ticks > b.ticks) ? (a.ticks - b.ticks) : (b.ticks - a.ticks);
120+
return Duration::fromSeconds(static_cast<f64>(tick_diff) / 1000000.0);
121+
}
122+
123+
class Timer {
124+
public:
125+
Timer();
126+
~Timer();
127+
128+
void start();
129+
void stop();
130+
void reset();
131+
132+
Duration elapsed() const;
133+
Duration tick();
134+
135+
bool isRunning() const;
136+
137+
private:
138+
TimePoint m_start_time;
139+
TimePoint m_stop_time;
140+
u64 m_accumulated_ticks;
141+
bool m_is_running;
142+
};
143+
144+
class ScopeTimer {
145+
public:
146+
explicit ScopeTimer(const char* name);
147+
~ScopeTimer();
148+
149+
private:
150+
const char* m_name;
151+
Timer m_timer;
152+
};
153+
154+
}

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ add_executable(CaffeineTest
55
test_containers.cpp
66
test_math.cpp
77
test_core.cpp
8+
test_timer.cpp
89
)
910

1011
target_link_libraries(CaffeineTest PRIVATE Caffeine)

0 commit comments

Comments
 (0)