From Gnash Project Wiki
Movie advancement in Gnash is handled by calling VM::get().getRoot().advance() at regular intervals. So far, Gnash core lib is based on the assumption that the ::advance function is called at FPS rate.
Pitfalls in current implementation
This approach is bogus, and it's likely that the original author thought about this. Last statement is due to the fact that the advance() methods take a float as the only argument, while Gnash currently throws away that information. It is likely that the argument was there to specify an amount of time to advance the movie, whereas the ::advance() function should only execute the *frame* advancement if enough time is elapsed since last frame advancement (FPS).
Making ::advance() call rate depend on FPS has the following problems:
- Core events can only be triggered at FPS boundary
- Sound can only be controlled at FPS boundary
- Interval timeouts can only be triggered at FPS boundary
- User events can only be triggered at either FPS boundary OR at a rate decided by the GUI, making immediate response to user intervention possibly overrated (consider fast clicks)
- Video frames can only be rendered at FPS boundary
- The core lib vision of time isn't driven by caller, so can only rely on real-time (for example: in threads synchonization)
A quick refactoring might be having two distinct functions:
- advance - for frame advancement
- tick - for heart beating
The tick() function would return a boolean stating whether redisplay is wanted. Video character instances, for example, might want a redraw when new video frames arrives. Intervals forcing redisplay (updateAfterEvent) might also want a redisplay.
The tick() function would be the one processing LoadVars and intervals.
The core would still know what the virtual time is since we're using a VirtualClock.
Tests show that the heart-beating time should be 1/10 of the FPS time.
See Patch #6339 for a patch (the patch uses 1/20, easy to change)
Note that in an 'heart-beating' model (or time-slices) we may also assign time slots to input consuming, which would open the opportunity for single-threaded versions of gnash.
The time elapsed is passed to the single ::advance method of movie_root. This time is used to decide what to do at time of that call. In any case the core timers would be updated (VM time) and we'd check for any expired timer. If the time elapses FPS boundary we also advance to next frame. Somthing to be careful about is that we need to be called as close as possible to FPS rate or animation wouldn't be smooth.
fixed rate "slices"
This design is based on the informations provided at http://www.craftymind.com/2008/04/18/updated-elastic-racetrack-for-flash-9-and-avm2/
Instead of running at the nominal fps rate defined in the .SWF file we let the core run the main loop at a fixed rate, independent of the SWF file. This rate could be something like 50 iterations / sec by default but should be configurable via the config file.
The fundamental idea is that frame advancing, event processing and rendering all run asynchronously.
In each iteration of the main loop the core (GUI?) does these steps:
- (debatable) consume part of pending input sources
- check mouse and keyboard events and handle them if necessary
- run any expired Timer/Interval
- check if the inter-frame time has elapsed and advance frames
- render if any of the previous steps did invalidate the stage (and updateAfterEvent flag is set, for certain cases)
- The above mechanism does not work when the nominal fps rate is > the slice rate. The movie will be limited to the slice rate in that case. One approach could be to advance multiple frames in step 4 but this leads to a new problem:
- the proprietary player still executes frame code at the correct times, ie. getTimer and Date() advance at the nominal frame rate; this would require additional timings to the second step
- quick tests show that the proprietary player executes very close (but not exactly) to the desired fps rate. It doesn't seem to suffer from aliasing effects (would be noticeable when fps = slice rate -1) because the inter-frame time appears to be nearly constant. This is an indication that the slice approach is not (fully) correct; more tests needed, however