raze/source/build/include/clockticks.hpp
pogokeen 222e593437 Change some clocks to support sub-tick precision using a new class, ClockTicks.
Using that higher precision, interpolate at a higher granularity.
Further, truncate the target interpolation time to vertical blank boundaries to avoid producing temporal artifacts.
Fix issues caused by interpolation calculations being handled differently in multiple places (and fix cases where smoothratios were being thrown away only to be redone without checking all proper conditions).
Ensure ClockTicks changes do not break other targets (EKenBuild, VoidSW), but note any interpolation there is not similarly updated.

git-svn-id: https://svn.eduke32.com/eduke32@8050 1a8010ca-5511-0410-912e-c29ae57300e0

# Conflicts:
#	source/build/src/build.cpp
#	source/build/src/sdlayer.cpp
2019-09-20 21:09:14 +02:00

153 lines
5.2 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 <chrono>
int timerGetRate(void);
//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);
class ClockTicks
{
public:
ClockTicks() : ClockTicks(0, 0) {};
ClockTicks(int32_t ticks) : ClockTicks(ticks, 0) {};
ClockTicks(int32_t ticks, uint32_t 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 fraction)
{
return ticksS32 = (ticksS32 & WHOLE_MASK) | ((ticksS32 < 0) ? ((int64_t) 0 - fraction) & FRACTION_16_MASK : fraction) << 16;
}
int64_t set(int32_t ticks, uint16_t 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;
}
ClockTicks& setFromScale16(int64_t ticksScale16)
{
ticksS32 = ticksScale16 << 16;
update();
return *this;
}
static ClockTicks fromScale16(int64_t ticksScale16)
{
ClockTicks ct;
ct.setFromScale16(ticksScale16);
return ct;
}
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 rhs)
{
ticksS32 = ticksS32 << rhs;
update();
return *this;
};
ClockTicks& operator>>=(int32_t 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.ticksS32 == rhs.ticksS32; }
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.ticksS32 < rhs.ticksS32; }
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 const uint64_t VALUE_MASK = 0xFFFFFFFFFFFF0000ull;
static const uint64_t WHOLE_MASK = 0xFFFFFFFF00000000ull;
static const uint64_t FRACTION_MASK = 0x00000000FFFF0000ull;
static const uint64_t FRACTION_16_MASK = 0x000000000000FFFFull;
inline void update()
{
wholeTicks = ticksS32 >> 32;
ticksS32 &= VALUE_MASK;
}
};
#endif /* CLOCKTICKS_HPP_ */