Timing

libavg updates the display at regular intervals. The update frequency can be set by using Player.setFramerate() or Player.setVBlankFramerate(). Ideally, the updates should be synchronized with the refresh rate of the monitor to avoid judders and horizontal artifacts in the image displayed. Today's TFT monitors usually refresh at 60 Hz, and Player.setVBlankFramerate(1) will attempt to schedule one display update per monitor refresh (this is the default). Unfortunately, there are a few graphics drivers with bugs in this regard - see below for some hints. In these cases, Player.setFramerate() can be used to set a fixed update frequency.

Events are handled once per update as well. That means, for instance, that intervals with a frequency higher than the framerate aren't possible - such an interval callback will never be called more than once per frame. It also means that conceptually, all events in one update are called at the same instant in time. In consequence, Player.getFrameTime() returns the same time for all callbacks in one update. The order in which the callbacks are called in one frame is undefined, but libavg tries not to change the order from one frame to the next. Internal time starts at zero when play() is called and progresses with wall time from that point on.

Unless...well, unless you call Player.setFakeFPS() with a parameter other than -1, which makes libavg ignore real time and 'fake' a framerate. This is useful if you need to record animations or for test code that always needs to produce the same results regardless of the performance of the maschine it's running on.

(Aside: For best results with videos, run them at the same (or half the) framerate that the player itself is running at. Obviously, the videos should be encoded for the framerate you're going to run it at. In a pinch, you can make a video run slower or faster using the fps attribute of the video node.)

Timed Callbacks

(!) Note: (!)

the following text serves to describe libavg timer handling.
In many cases, there are better ways to create smooth animations quickly: See Animation.

Two methods are available to have a routine called regularly: setOnFrameHandler() and setInterval(). If you've done any html/javascript coding, setInterval() (and setTimeout() as well) might sound familiar. Using these routines, it is possible to set up animations.

So, as an example, we'll set up a naive animation using setOnFrameHandler():

timer2.py:

 1#!/usr/bin/env python
 2# -*- coding: utf-8 -*-
 3
 4from libavg import avg
 5
 6def moveText():
 7    global node
 8    if node.x < 200:
 9        node.x += 1
10
11player = avg.Player.get()
12
13canvas = player.createMainCanvas(size=(640,480))
14rootNode = canvas.getRootNode()
15node = avg.WordsNode(pos=(10,10), font="arial", 
16        text="Hello World", parent=rootNode)
17player.setOnFrameHandler(moveText)
18
19player.play()

moveText() gets called after every screen update that libavg executes. Because of the setFramerate() call, the text should move right by one pixel for each monitor refresh. Since this is a program that doesn't tax the system, it will probably work. Missed frames, however, will cause more jerkyness than necessary - and in this case the animation will last longer until it's done, making it hard to sync other things to it. Also, the animation will take more or less time depending on the monitor refresh rate, which is probably not what you want.

To solve these issues, you need to make animations time-based in most real-life scenarios. Programs that calculate the position of an object using elapsed time are much more resilient to less-than-ideal conditions. Use getFrameTime() to get the current simulation time. In cases such as these, where the actual animation logic is not very complicated, the best option is to use the animation framework.

Anyway, back to the callbacks. Making the text jump a few times until it's reached it's destination involves setInterval():

timer3.py:

 1#!/usr/bin/env python
 2# -*- coding: utf-8 -*-
 3
 4from libavg import avg
 5
 6def moveText():
 7    global node
 8    if node.x < 200:
 9        node.x += 20
10    else:
11        player.clearInterval(timer)
12
13player = avg.Player.get()
14
15canvas = player.createMainCanvas(size=(640,480))
16rootNode = canvas.getRootNode()
17node = avg.WordsNode(pos=(10,10), font="arial", text="Hello World", parent=rootNode)
18timer = player.setInterval(200, moveText)
19
20player.play()

The setInterval() call tells libavg to invoke moveText() every 200 milliseconds, resulting in pretty jerky movement. Also, clearInterval() is called when the animation is finished so the callback isn't invoked anymore.

Synchronizing Refreshes

Syncronizing display updates to the monitor refresh is something that should be easy. By default, libavg attempts to do one display update per monitor refresh. This can be changed by calling Player.setVBlankFramerate() or Player.setFramerate(). As long as there are no driver bugs involved, this is easy and consistent.

On Mac OS X, it works as long as there's only one display in the system. If libavg is running on a second monitor, the system still syncronizes to the first monitor. setVBlankFramerate() with a parameter other than 1 isn't supported on OS X.

Under Linux, nvidia cards usually have working vblank support, but there have been driver versions with broken support. We've also seen nvidia driver/kernel combinations in which internal libavg vblank support is broken but doing an

1export __GL_SYNC_TO_VBLANK=1

works in conjunction with setFramerate(60).

Quite a few drivers do busy waiting to implement refresh syncing. This will cause the load of one CPU core to go to 100% - the driver is spending time in an empty loop waiting for the display.