载入中...
搜索中...
未找到
Time.cpp
浏览该文件的文档.
1#include "common/Time.h"
2
3#include <cmath>
4#include <limits>
5#include <utility>
6
7namespace eve {
8namespace {
9
10Diagnostic invalidTime(std::string message) {
12}
13
14} // namespace
15
17 if (!std::isfinite(seconds)) return Result<Duration>::failure(invalidTime("duration seconds must be finite"));
18
19 const long double nanoseconds = static_cast<long double>(seconds) * 1000000000.0L;
20 constexpr long double minimum = static_cast<long double>(std::numeric_limits<std::int64_t>::min());
21 constexpr long double maximum = static_cast<long double>(std::numeric_limits<std::int64_t>::max());
22 if (nanoseconds < minimum || nanoseconds > maximum)
23 return Result<Duration>::failure(invalidTime("duration seconds are out of range"));
24
25 return Result<Duration>::success(Duration(static_cast<std::int64_t>(std::round(nanoseconds))));
26}
27
28double Duration::seconds() const noexcept { return static_cast<double>(nanoseconds_) / 1000000000.0; }
29
31 if ((other.nanoseconds_ > 0 && nanoseconds_ > std::numeric_limits<std::int64_t>::max() - other.nanoseconds_) ||
32 (other.nanoseconds_ < 0 && nanoseconds_ < std::numeric_limits<std::int64_t>::min() - other.nanoseconds_))
33 return Result<Duration>::failure(invalidTime("duration addition overflow"));
34 return Result<Duration>::success(Duration(nanoseconds_ + other.nanoseconds_));
35}
36
38 if (!std::isfinite(rate) || rate < 0.0)
39 return Result<Duration>::failure(invalidTime("duration rate must be finite and non-negative"));
40 const long double nanoseconds = static_cast<long double>(nanoseconds_) * static_cast<long double>(rate);
41 constexpr long double minimum = static_cast<long double>(std::numeric_limits<std::int64_t>::min());
42 constexpr long double maximum = static_cast<long double>(std::numeric_limits<std::int64_t>::max());
43 if (nanoseconds < minimum || nanoseconds > maximum)
44 return Result<Duration>::failure(invalidTime("scaled duration is out of range"));
45 return Result<Duration>::success(Duration(static_cast<std::int64_t>(std::round(nanoseconds))));
46}
47
49 if (nanoseconds_ < earlier.nanoseconds_)
51 Diagnostic::error(DiagnosticCode::PreconditionViolation, "monotonic source moved backwards"));
52
53 // Subtract in the unsigned domain after the ordering check. The modulo
54 // subtraction is the exact distance for two ordered int64 values, while
55 // the explicit bound check prevents converting a distance larger than the
56 // Duration domain back to int64_t.
57 const std::uint64_t distance =
58 static_cast<std::uint64_t>(nanoseconds_) - static_cast<std::uint64_t>(earlier.nanoseconds_);
59 if (distance > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()))
61 "monotonic timestamp distance exceeds Duration range"));
62 return Result<Duration>::success(Duration(static_cast<std::int64_t>(distance)));
63}
64
65SimulationClock::SimulationClock(ITimeSource& source, Duration fixedStep) : source_(source), fixedStep_(fixedStep) {
66 EV_ASSERT(fixedStep_.nanoseconds() > 0, "SimulationClock fixed step must be positive");
67 if (fixedStep_.nanoseconds() <= 0) fixedStep_ = Duration::fromNanoseconds(16666667);
68}
69
71 if (fixedStep.nanoseconds() <= 0)
72 return Result<void>::failure(invalidTime("fixed simulation step must be positive"));
73 fixedStep_ = fixedStep;
74 return Result<void>::success();
75}
76
78 if (!std::isfinite(rate) || rate < 0.0)
79 return Result<void>::failure(invalidTime("simulation rate must be finite and non-negative"));
80 rate_ = rate;
81 return Result<void>::success();
82}
83
85 if (realDelta.nanoseconds() < 0)
87 invalidTime("simulation frame duration must be non-negative"));
88
89 auto nextFrame = frame_.incremented();
90 if (!nextFrame)
92 Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation frame index overflow"));
93
94 auto scaled = realDelta.scaled(rate_);
95 if (!scaled) return Result<std::vector<SimulationStep>>::failure(scaled.status());
96 const Duration scaledDelta = std::move(scaled).takeValue();
97
98 if (paused_) {
99 frame_ = *nextFrame;
100 lastFrameDelta_ = realDelta;
101 return Result<std::vector<SimulationStep>>::success({});
102 }
103
104 auto total = accumulator_.tryAdd(scaledDelta);
105 if (!total) return Result<std::vector<SimulationStep>>::failure(total.status());
106 const std::int64_t accumulated = std::move(total).takeValue().nanoseconds();
107 const std::uint64_t accumulatedUnsigned = static_cast<std::uint64_t>(accumulated);
108 const std::uint64_t fixedStepUnsigned = static_cast<std::uint64_t>(fixedStep_.nanoseconds());
109 const std::uint64_t stepCount = accumulatedUnsigned / fixedStepUnsigned;
110 if (stepCount > std::numeric_limits<std::size_t>::max() ||
111 stepCount > std::numeric_limits<std::uint64_t>::max() - tick_.value())
112 return Result<std::vector<SimulationStep>>::failure(
113 Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation step count overflow"));
114
115 // Avoid an unbounded allocation when a stalled frame or a malicious
116 // replay requests more catch-up steps than one result can safely own.
117 constexpr std::uint64_t maxStepsPerAdvance = 1000000;
118 if (stepCount > maxStepsPerAdvance)
119 return Result<std::vector<SimulationStep>>::failure(
120 Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation catch-up step limit exceeded"));
121 if (stepCount > std::numeric_limits<std::uint64_t>::max() / fixedStepUnsigned)
122 return Result<std::vector<SimulationStep>>::failure(
123 Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation duration multiplication overflow"));
124 const std::uint64_t consumedUnsigned = stepCount * fixedStepUnsigned;
125 const std::uint64_t remainderUnsigned = accumulatedUnsigned - consumedUnsigned;
126 if (remainderUnsigned > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()))
127 return Result<std::vector<SimulationStep>>::failure(
128 Diagnostic::error(DiagnosticCode::InvariantViolation, "simulation remainder exceeds Duration range"));
129
130 std::vector<SimulationStep> steps;
131 steps.reserve(static_cast<std::size_t>(stepCount));
132 for (std::uint64_t i = 0; i < stepCount; ++i) steps.push_back({SimulationTick(tick_.value() + i + 1), fixedStep_});
133
134 tick_ = SimulationTick(tick_.value() + stepCount);
135 accumulator_ = Duration(static_cast<std::int64_t>(remainderUnsigned));
136 frame_ = *nextFrame;
137 lastFrameDelta_ = realDelta;
138 return Result<std::vector<SimulationStep>>::success(std::move(steps));
139}
140
142 const MonotonicTimestamp now = source_.monotonicNow();
143 if (!previousSourceTime_) {
144 auto first = advance(Duration::zero());
145 if (!first) return first;
146 previousSourceTime_ = now;
147 return first;
148 }
149
150 auto elapsed = now.since(*previousSourceTime_);
151 if (!elapsed) return Result<std::vector<SimulationStep>>::failure(elapsed.status());
152 Duration delta = std::move(elapsed).takeValue();
153 auto result = advance(delta);
154 if (!result) return result;
155 previousSourceTime_ = now;
156 return result;
157}
158
160 tick_ = tick;
161 frame_ = FrameIndex::zero();
162 accumulator_ = Duration::zero();
163 lastFrameDelta_ = Duration::zero();
164 previousSourceTime_.reset();
165}
166
167} // namespace eve
#define EV_ASSERT(cond,...)
Assert an internal engine invariant (state that must always hold).
Definition Assert.h:37
std::string message
float maximum[3]
float minimum[3]
std::int32_t first
float distance
int steps
SimulationTick tick
Common time values and an injectable deterministic simulation clock.
const UnitySourceAsset & source
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Signed, fixed-resolution simulation duration in nanoseconds.
Definition Time.h:43
constexpr std::int64_t nanoseconds() const noexcept
Return the exact signed nanosecond representation.
Definition Time.h:69
Result< Duration > tryAdd(Duration other) const
Add two durations with overflow validation.
Definition Time.cpp:30
static Result< Duration > fromSeconds(double seconds)
Convert finite seconds to the nearest nanosecond.
Definition Time.cpp:16
static constexpr Duration fromNanoseconds(std::int64_t nanoseconds) noexcept
Construct an exact duration from nanoseconds.
Definition Time.h:55
constexpr Duration() noexcept=default
Construct a zero duration.
static constexpr Duration zero() noexcept
Return zero duration.
Definition Time.h:60
double seconds() const noexcept
Return this duration as seconds for legacy/presentation APIs.
Definition Time.cpp:28
Result< Duration > scaled(double rate) const
Scale a duration by a finite non-negative rate.
Definition Time.cpp:37
Injectable source for elapsed and metadata time.
Definition Time.h:173
virtual MonotonicTimestamp monotonicNow() const =0
Read the non-decreasing source-relative timestamp.
Timestamp from a monotonic source.
Definition Time.h:104
Result< Duration > since(MonotonicTimestamp earlier) const
Compute elapsed duration from an earlier timestamp.
Definition Time.cpp:48
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
double rate() const noexcept
Current slow-motion rate.
Definition Time.h:237
Result< void > setRate(double rate)
Set a finite non-negative simulation rate (1 is normal speed).
Definition Time.cpp:77
void reset(SimulationTick tick=SimulationTick::zero()) noexcept
Reset deterministic state to a supplied tick without reading a clock.
Definition Time.cpp:159
Duration fixedStep() const noexcept
Fixed duration of emitted simulation steps.
Definition Time.h:235
Result< std::vector< SimulationStep > > advance(Duration realDelta)
Advance from supplied monotonic elapsed duration without reading a clock.
Definition Time.cpp:84
Result< std::vector< SimulationStep > > sample()
Read the source and emit deterministic steps for one frame.
Definition Time.cpp:141
SimulationClock(ITimeSource &source, Duration fixedStep=Duration::fromNanoseconds(16666667))
Construct a fixed-step clock without reading the source.
Definition Time.cpp:65
Result< void > setFixedStep(Duration fixedStep)
Set the positive fixed-step duration; accumulated time is preserved.
Definition Time.cpp:70
constexpr std::optional< StrongUint64 > incremented() const noexcept
Returns the next value, or empty instead of unsigned wraparound.
constexpr std::uint64_t value() const noexcept
Returns the underlying value at an explicit protocol boundary.
static constexpr StrongUint64 zero() noexcept
Returns the zero value for this strong type.
eve::Diagnostic Diagnostic
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
detail::StrongUint64< detail::SimulationTickTag > SimulationTick
Deterministic simulation time step; it is not wall-clock time.
Definition Time.h:31