Using mode 15 (220x176, 16 colors) I noticed a strange behavior with frame rate. If I set the frame rate at 30, the FPS counter displays 25. If I set the frame rate at 60, the FPS counter displays 50. I tried to look a bit in the Pokitto lib but nothing obvious explains that.
My guess is that the display is 50Hz and there is some vsync?
It could be because in the update-function there is nextFrameMillis = getTime()+timePerFrame (and not nextFrameMillis += timePerFrame). So the next frame gets rendered timePerFrame milliseconds from when the previous update actually started, which could be a few milliseconds late.
Indeed there is a rounding error but in the âother directionâ. At 60 FPS the interval should be 16.666666 but because of the integer division the interval is 16. So it should be even a bit faster.
Indeed this seems to be the issue. Applying your proposed fix the FPS is correct.
I do believe itâs important to have the correct frame rate so I think it should be fixed. The issue is that this breaks compatibility fix existing games as they will run faster My proposal is to use a per-processor variable so that in My_settings.h itâs possible to add something like â#define ACCURATE_FPSâ to have the correct behavior. If you guys think itâs worth it I could do a pull request for that.
I think itâs worth recompiling some old games to see what the difference is before assuming anything.
I suspect for most games it wonât be much of an issue.
Ideally games shouldnât be locking their behaviour to the framerate anyway,
they should be measuring actual time.
There is at least one thing to consider before doing this change.
When the game starts, (nextFrameMillis initialized to 0) if it takes for example 5 seconds for the game to start running the update-loop, then the first 150+ frames are rendered in one burst until nextFrameMillis catches up with the timer. Same thing later during excecution if the game pauses updates while loading resources from sd card or something.
Adding a simple check might be sufficient:
if nextFrameMillis is lagging more than one frame, then use the current method and set its value to now+timePerFrame.
I tend to disagree. This is mostly true for âmodernâ games that use a floating point delta time. Personally even in this case I sometime makes assumption based on the frame, for example when applying friction to an entity (e.g. multiply velocity by 0.9 each frame).
But for retro game based on integer I think itâs critical to have known frame rate. If the players moves by 1 pixel every frame it should always result to the same movement speed. Another issue is related to the low resolution. Generally you need to move the entities at a constant speed (e.g. 2 pixel each frame) to prevent jaggerness. If you take delta time (that is constant between frames) into consideration the multiplication might result in entities not moving constantly (e.g. 2 pixel, 2 pixel, 3 pixel, 2 pixel, 2 pixel).
Not sure it can be fixed. The issue is that it seems we only have time in milliseconds, so we need to round the interval into a number of milliseconds. Oh do you mean rounding 16.666⌠into 17 instead of 16?
This is the first thing I noticed after patching the code. When the game started, it was super fast for few seconds before going to normal. To solve that I currently use a flag to know the first frame. When the first frame is played nextFrameMillis is set to current time.
Last issue, which Iâm not sure about,it feels like after the patch movement is a bit jaggier than before. I will need to investigate further. I think what happens is the delta slowly drifts and suddenly two frames are played to catch up. Iâm afraid this is not the end of the story.
The correct rounding to milliseconds would improve the situation. But to get rid of the cumulative error (or reducenit significantly) in the calculation ânextFrameMillis += timePerFrameâ would require to save those times in microseconds. Thought, I cannot say how much it really affects to FPS accuracy which however is in integer resolution.
You donât need to avoid float on the Pokitto,
unless youâve measured and it definitely isnât fast enough for your game.
Donât forget the Cortex-M0 is a 32-bit processor,
and memory isnât as much of an issue as on certain other systems.
But even then you only need it to accumulate time,
you donât have to use it for physics calculations.
You can have a fixed-time update step, e.g. 1 update every 10 milliseconds),
accumulate the delta time and only run an update step every time 10ms has elapsed.
(If 20ms has accumulated, do two update steps.)
(This is the âsemi-fixed timestepâ approach described in the famous fix your timestep article.)
Fixed point arithmetic is also an option if floating point wonât suffice.
That exactly the solution Iâm aiming for. Do anybody know how to get microseconds time?
Looking at the code currently getTime() use systick interrupt to increment an internal counter. The actual precision is not even milliseconds. The interrupt rate is 100Hz and the counter is increased by 10 each tick. Not sure why.
Mmh. Yeah it seems that getting microseconds with mbed is quite an issueâŚ
I tried to increase the systick rate to 1000Hz in order to fix the milliseconds precision of getTime(). There doesnât seem to be any performance issue. And moreover:
Having correct getTime() precision seems to fix this issue.
While studying PokittoLib and how the timer works, I found that itâs possible to get millisecond and even microsecond precision without having to increase the SysTick rate.
This is a simplified PokittoClock.cpp with interrupts at 100 ms interval. For precision it uses SysTick->VAL which is a counter value that counts down from 4800000-1 and overflows every 100 millisecs.
volatile uint32_t pt_count = 0;
extern "C" {
void SysTick_Handler(void) {
pt_count += 100; // increment by 100 milliseconds
}
}
void Core::initClock() {
// Reload value of 24 bit down counter. 4800000-1 gives 100 ms interval on a 48 MHz system
SysTick->LOAD = 4800000-1;
SysTick->VAL = 0;
SysTick->CTRL = 4 | 2 | 1;
pt_count = 0;
}
uint32_t Core::getTime() {
// Convert SysTick counter value to milliseconds since last interrupt:
// systick_ms = (4800000-1-SysTick->VAL)/48000
uint32_t systick_ms = ((((4800000-1-SysTick->VAL)>>9)*699)>>16); // div by 48000 => mul by 699>>25
return pt_count + systick_ms;
}
uint32_t Core::getTime_us() {
// Convert SysTick counter value to microseconds since last interrupt:
// systick_us = (4800000-1-SysTick->VAL)/48
uint32_t systick_us = ((((4800000-1-SysTick->VAL)>>5)*21845)>>15); // div by 48 => mul by 21845>>20
return pt_count*1000 + systick_us;
}
This works on hardware, but on emulator my test program halts just before reaching 90 seconds. Probably the timer gives some weird values that make the update() function stop updating. Also with the unmodified PokittoClock.cpp there is some hiccup at 90 seconds on emulator but it continues after a while.
Is extern "C" needed?
I thought the interrupts could handle name-mangled functions.
Also, some of these magic numbers could do with being changed to constexpr variables with nice names so itâs clearer what theyâre supposed to mean, and perhaps some comments to explain how the numbers are derived and whatâs actually happening here.
(Iâm presuming youâre treating the numbers as Q16.16 fixed points or something.)