raze-gles/source/build/include/clockticks.hpp
Christoph Oelckers 481ac965cf - reverted ClockTicks to an integer so that interpolation can be reimplemented without it affecting the global game timer.
The entire method at use here is essentially not correct. Interpolation should be handled independently of the game timer directly based on the underlying clock, like in ZDoom.
There's interpolation bugs in the Build games that cannot be fixed if totalclock is used for it, but if we use something else we do not need a fractional totalclock.
2020-07-14 11:52:38 +02:00

167 lines
5.6 KiB
C++

/*
* clockticks.hpp
* ClockTicks is a class that tracks signed ticks & fractional ticks for
* high granularity game clocks that are backwards-compatible with
* Build integer timing
*
* Copyright © 2019, Alex Dawson. All rights reserved.
*/
#ifndef CLOCKTICKS_HPP_
#define CLOCKTICKS_HPP_
#include "timer.h"
//POGO: BUILD/EDuke32 uses right shifts on signed variables expecting arithmetic shifts.
// This was already non-portable, and we carry that assumption forth here
// (so we might as well check it).
EDUKE32_STATIC_ASSERT(-1 >> 1 == -1);
#if 0
class ClockTicks
{
public:
ClockTicks() : ClockTicks(0, 0) {};
ClockTicks(int32_t const ticks) : ClockTicks(ticks, 0) {};
ClockTicks(int32_t const ticks, uint32_t const fraction) { set(ticks, fraction); };
ClockTicks(const ClockTicks&) = default;
int64_t getFraction() const
{
return (ticksS32 & FRACTION_MASK) >> 16 | ((ticksS32 < 0) ? VALUE_MASK : 0);
}
int64_t setFraction(uint16_t const fraction)
{
return ticksS32 = (ticksS32 & WHOLE_MASK) | ((ticksS32 < 0) ? ((int64_t) 0 - fraction) & FRACTION_16_MASK : fraction) << 16;
}
int64_t set(int32_t const ticks, uint16_t const fraction)
{
ticksS32 = ((uint64_t) ticks) << 32 | ((ticks < 0) ? ((int64_t) 0 - fraction) & FRACTION_16_MASK : fraction) << 16;
update();
return ticksS32;
}
int64_t toScale16() const
{
return ticksS32 >> 16;
}
double toScale16F() const
{
return ticksS32 * (1/(65536.*65536.));
}
ClockTicks& setFromScale16(int64_t const ticksScale16)
{
ticksS32 = ticksScale16 << 16;
update();
return *this;
}
static ClockTicks fromScale16(int64_t const ticksScale16)
{
ClockTicks ct;
ct.setFromScale16(ticksScale16);
return ct;
}
// returns 0 if equal, < 0 if a < b, > 0 if a > b
static int64_t compareHighPrecision(ClockTicks const a, ClockTicks const b)
{
ClockTicks delta = a - b;
return delta.toScale16();
}
ClockTicks& operator=(const ClockTicks& rhs)
{
ticksS32 = rhs.ticksS32;
update();
return *this;
};
ClockTicks& operator+=(const ClockTicks& rhs)
{
ticksS32 += rhs.ticksS32;
update();
return *this;
};
ClockTicks& operator-=(const ClockTicks& rhs)
{
ticksS32 -= rhs.ticksS32;
update();
return *this;
};
ClockTicks& operator*=(const ClockTicks& rhs)
{
ticksS32 = (ticksS32>>16)*(rhs.ticksS32>>16) >> 16;
update();
return *this;
};
ClockTicks& operator/=(const ClockTicks& rhs)
{
ticksS32 = ticksS32/rhs.ticksS32 << 32;
update();
return *this;
};
ClockTicks& operator%=(const ClockTicks& rhs)
{
ticksS32 %= rhs.ticksS32;
update();
return *this;
};
ClockTicks& operator<<=(int32_t const rhs)
{
ticksS32 = ticksS32 << rhs;
update();
return *this;
};
ClockTicks& operator>>=(int32_t const rhs)
{
ticksS32 = ticksS32 >> rhs;
update();
return *this;
};
friend ClockTicks operator+(ClockTicks lhs, const ClockTicks& rhs) { return lhs += rhs; }
friend ClockTicks operator-(ClockTicks lhs, const ClockTicks& rhs) { return lhs -= rhs; }
friend ClockTicks operator-(ClockTicks val) { return ClockTicks(0) -= val; }
friend ClockTicks operator*(ClockTicks lhs, const ClockTicks& rhs) { return lhs *= rhs; }
friend ClockTicks operator/(ClockTicks lhs, const ClockTicks& rhs) { return lhs /= rhs; }
friend ClockTicks operator%(ClockTicks lhs, const ClockTicks& rhs) { return lhs %= rhs; }
friend ClockTicks operator<<(ClockTicks lhs, int32_t rhs) { return lhs >>= rhs; }
friend ClockTicks operator>>(ClockTicks lhs, int32_t rhs) { return lhs <<= rhs; }
friend inline bool operator==(const ClockTicks& lhs, const ClockTicks& rhs) { return lhs.wholeTicks == rhs.wholeTicks; }
friend inline bool operator!=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs == rhs); }
friend inline bool operator<(const ClockTicks& lhs, const ClockTicks& rhs) { return lhs.wholeTicks < rhs.wholeTicks; }
friend inline bool operator>(const ClockTicks& lhs, const ClockTicks& rhs) { return rhs < lhs; }
friend inline bool operator<=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs > rhs); }
friend inline bool operator>=(const ClockTicks& lhs, const ClockTicks& rhs) { return !(lhs < rhs); }
explicit operator uint32_t() const { return wholeTicks; };
explicit operator int32_t() const { return wholeTicks; };
private:
//POGO: wholeTicks must be first in member-order to ensure the address of
// ClockTicks can be treated as a pointer to int32_t.
int32_t wholeTicks;
//POGO: Organize our bits as if we're scaled to have an additional 32-bits
// of fractional precision so that we can handle overflows in a
// way that is accurate to the original BUILD int32_t expectation.
// Multiplication overflows best with 16-bits of fractional precision,
// so only promise that much publicly.
int64_t ticksS32;
static constexpr uint64_t const VALUE_MASK = 0xFFFFFFFFFFFF0000ull;
static constexpr uint64_t const WHOLE_MASK = 0xFFFFFFFF00000000ull;
static constexpr uint64_t const FRACTION_MASK = 0x00000000FFFF0000ull;
static constexpr uint64_t const FRACTION_16_MASK = 0x000000000000FFFFull;
inline void update()
{
wholeTicks = ticksS32 >> 32;
ticksS32 &= VALUE_MASK;
}
};
#else
using ClockTicks = int;
#endif
#endif /* CLOCKTICKS_HPP_ */