Refactor nextFrame() and add setFrameDuration()

nextFrame() changes were in colaboration with @MrBlinky

setFrameDuration() can be used as an alternative to setFrameRate()
This commit is contained in:
Scott Allen 2018-02-09 18:18:19 -05:00
parent 4788b1ab01
commit 460e768ea9
3 changed files with 52 additions and 32 deletions

View File

@ -83,6 +83,7 @@ readUnitName KEYWORD2
safeMode KEYWORD2
saveOnOff KEYWORD2
setCursor KEYWORD2
setFrameDuration KEYWORD2
setFrameRate KEYWORD2
setRGBled KEYWORD2
setTextBackground KEYWORD2

View File

@ -19,13 +19,9 @@ Arduboy2Base::Arduboy2Base()
currentButtonState = 0;
previousButtonState = 0;
// frame management
setFrameRate(60);
frameCount = -1;
nextFrameStart = 0;
setFrameDuration(16);
frameCount = 0;
justRendered = false;
// init not necessary, will be reset after first use
// lastFrameStart
// lastFrameDurationMs
}
// functions called here should be public so users can create their
@ -185,6 +181,11 @@ void Arduboy2Base::setFrameRate(uint8_t rate)
eachFrameMillis = 1000 / rate;
}
void Arduboy2Base::setFrameDuration(uint8_t duration)
{
eachFrameMillis = duration;
}
bool Arduboy2Base::everyXFrames(uint8_t frames)
{
return frameCount % frames == 0;
@ -192,38 +193,27 @@ bool Arduboy2Base::everyXFrames(uint8_t frames)
bool Arduboy2Base::nextFrame()
{
unsigned long now = millis();
bool tooSoonForNextFrame = now < nextFrameStart;
uint8_t now = (uint8_t) millis();
uint8_t frameDurationMs = now - thisFrameStart;
if (justRendered) {
lastFrameDurationMs = now - lastFrameStart;
lastFrameDurationMs = frameDurationMs;
justRendered = false;
return false;
}
else if (tooSoonForNextFrame) {
// if we have MORE than 1ms to spare (hence our comparison with 2),
// lets sleep for power savings. We don't compare against 1 to avoid
// potential rounding errors - say we're actually 0.5 ms away, but a 1
// is returned if we go to sleep we might sleep a full 1ms and then
// we'd be running the frame slighly late. So the last 1ms we stay
// awake for perfect timing.
// This is likely trading power savings for absolute timing precision
// and the power savings might be the better goal. At 60 FPS trusting
// chance here might actually achieve a "truer" 60 FPS than the 16ms
// frame duration we get due to integer math.
// We should be woken up by timer0 every 1ms, so it's ok to sleep.
if ((uint8_t)(nextFrameStart - now) >= 2)
else if (frameDurationMs < eachFrameMillis) {
// Only idle if at least a full millisecond remains, since idle() may
// sleep the processor until the next millisecond timer interrupt.
if (++frameDurationMs < eachFrameMillis) {
idle();
}
return false;
}
// pre-render
justRendered = true;
lastFrameStart = now;
nextFrameStart = now + eachFrameMillis;
thisFrameStart = now;
frameCount++;
return true;

View File

@ -660,17 +660,47 @@ class Arduboy2Base : public Arduboy2Core
*
* \details
* Set the frame rate, in frames per second, used by `nextFrame()` to update
* frames at a given rate. If this function isn't used, the default rate will
* be 60.
* frames at a given rate. If this function or `setFrameDuration()`
* isn't used, the default rate will be 60 (actually 62.5, see note below).
*
* Normally, the frame rate would be set to the desired value once, at the
* start of the game, but it can be changed at any time to alter the frame
* update rate.
*
* \see nextFrame()
* \note
* \parblock
* The given rate is internally converted to a frame duration in milliseconds,
* rounded down to the nearest integer. Therefore, the actual rate will be
* equal to or higher than the rate given.
* For example, 60 FPS would be 16.67ms per frame. This will be rounded down
* to 16ms, giving an actual frame rate of 62.5 FPS.
* \endparblock
*
* \see nextFrame() setFrameDuration()
*/
void setFrameRate(uint8_t rate);
/** \brief
* Set the frame rate, used by the frame control functions, by giving
* the duration of each frame.
*
* \param duration The desired duration of each frame in milliseconds.
*
* \details
* Set the frame rate by specifying the duration of each frame in
* milliseconds. This is used by `nextFrame()` to update frames at a
* given rate. If this function or `setFrameRate()` isn't used,
* the default will be 16ms per frame.
*
* Normally, the frame rate would be set to the desired value once, at the
* start of the game, but it can be changed at any time to alter the frame
* update rate.
*
* \see nextFrame() setFrameRate()
*/
void setFrameDuration(uint8_t duration);
/** \brief
* Indicate that it's time to render the next frame.
*
@ -694,7 +724,7 @@ class Arduboy2Base : public Arduboy2Core
* }
* \endcode
*
* \see setFrameRate() nextFrameDEV()
* \see setFrameRate() setFrameDuration() nextFrameDEV()
*/
bool nextFrame();
@ -1120,8 +1150,7 @@ class Arduboy2Base : public Arduboy2Core
// For frame funcions
uint8_t eachFrameMillis;
unsigned long lastFrameStart;
unsigned long nextFrameStart;
uint8_t thisFrameStart;
bool justRendered;
uint8_t lastFrameDurationMs;
};