Line data Source code
1 : #pragma once
2 :
3 : #include <jage/engine/memory/cacheline_size.hpp>
4 : #include <jage/engine/time/events/snapshot.hpp>
5 : #include <jage/engine/time/hertz.hpp>
6 :
7 : #include <jage/engine/time/internal/concepts/real_number_time_source.hpp>
8 :
9 : #include <cmath>
10 : #include <cstdint>
11 : #include <stdexcept>
12 :
13 : namespace jage::engine::time::internal {
14 :
15 : template <internal::concepts::real_number_time_source TTimeSource>
16 : requires(TTimeSource::is_steady)
17 : class clock {
18 : using duration_ = typename TTimeSource::duration;
19 : using snapshot_ = events::snapshot<duration_>;
20 : static_assert(memory::cacheline_size >= sizeof(snapshot_));
21 : static_assert(alignof(snapshot_) == memory::cacheline_size);
22 : static_assert(sizeof(snapshot_) % memory::cacheline_size == 0);
23 : duration_ elapsed_time_{};
24 : duration_ tick_duration_{};
25 : std::uint64_t elapsed_ticks_{};
26 : double time_scale_{1.0};
27 :
28 : [[nodiscard]] auto
29 52 : ticks(const duration_ current_time) const -> std::uint64_t {
30 52 : const auto accumulated_time = current_time - elapsed_time_;
31 52 : return static_cast<std::uint64_t>(
32 52 : std::floor(accumulated_time.count() / tick_duration_.count())) +
33 52 : elapsed_ticks_;
34 : }
35 :
36 : public:
37 : using duration_type = duration_;
38 : using snapshot_type = snapshot_;
39 :
40 8 : constexpr clock(const hertz &cycles)
41 8 : : tick_duration_{static_cast<duration_type>(cycles)} {};
42 :
43 67 : [[nodiscard]] auto real_time() const -> duration_type {
44 67 : return TTimeSource::now().time_since_epoch();
45 : }
46 :
47 42 : [[nodiscard]] auto ticks() const -> std::uint64_t {
48 42 : return ticks(real_time() * time_scale_);
49 : }
50 :
51 6 : [[nodiscard]] auto game_time() const -> duration_type {
52 6 : return duration_type{ticks() * tick_duration_.count()};
53 : }
54 :
55 : [[nodiscard]] constexpr auto
56 6 : tick_duration() const noexcept -> const duration_type & {
57 6 : return tick_duration_;
58 : }
59 :
60 13 : auto set_time_scale(const double scale) -> void {
61 13 : if (scale < 0.0) [[unlikely]] {
62 1 : throw std::invalid_argument(
63 : "Refusing to set time scale to a negative value.");
64 : }
65 12 : elapsed_ticks_ = ticks();
66 12 : elapsed_time_ = real_time() * scale;
67 :
68 12 : time_scale_ = scale;
69 12 : }
70 :
71 10 : [[nodiscard]] auto snapshot() const -> snapshot_type {
72 10 : const auto current_real_time = real_time();
73 10 : const auto scaled_real_time = current_real_time * time_scale_;
74 10 : const auto accumulated_time = scaled_real_time - elapsed_time_;
75 10 : const auto accumulated_ticks =
76 10 : std::floor(accumulated_time / tick_duration_);
77 10 : const auto current_ticks = ticks(scaled_real_time);
78 : return {
79 : .real_time = current_real_time,
80 : .tick_duration = tick_duration_,
81 10 : .time_scale = time_scale_,
82 : .elapsed_time = elapsed_time_,
83 10 : .elapsed_frames = elapsed_ticks_,
84 : .frame = current_ticks,
85 : .accumulated_time =
86 20 : accumulated_time - accumulated_ticks * tick_duration_,
87 20 : };
88 : }
89 : };
90 :
91 : } // namespace jage::engine::time::internal
|