I am experimenting with a double buffered roScreen & 2d animation. I think I have misunderstood what SwapBuffers() does...
If my animation takes less than 16.6 mS (60FPS) from the previous SwapBuffers call then the next SwapBuffers call blocks until +16.6 mS, which is what I expected. However, if my animation takes a bit longer than 16.6 mS then the next SwapBuffers returns immediately! I was expecting it to block till the following sync at +33.3mS (30FPS).
Configuration: Model: 2400SK, Firmware Version: 075.05E00430A
I have reduced the original code to a demo that displays the measured frame timings on the screen and allows me to adjust the animation time using LEFT & RIGHT buttons. Its a bit bloaty, the avg filter is not really needed to see the problem, but here it all is:-
Library "v30/bslCore.brs"
sub main ()
' Initialise the usual 2d resources
'
ecodes = bslUniversalControlEventCodes()
clk = CreateObject("roTimespan")
port = CreateObject("roMessagePort")
screen = CreateObject("roScreen", TRUE) ' dbl buffered
screen.SetMessagePort(port)
screen.clear(0)
' Let's have a font to display the timings on screen
'
font = CreateObject("roFontRegistry").GetDefaultFont()
txt_height = font.getOneLineHeight()
txt_color = &h44ff44ff
txt_margin = 20
' Artificial animation time.
' This can be adjusted using LEFT/RIGHT buttons to explore the
' frame sync problem.
'
busy_ms = 8
' Frame timing data.
'
' The filter is just a luxury - but its reassuring to see
' it settle close to 16,666 uSecs in the 60FPS regime!
'
last_frame_start = INVALID ' monitor inter-frame period
frame_avg_filter = jcEWMAFilter(0.99, 1000/60)
' Spin around SwappingBuffers and monitoring
' how long each cycle takes.
'
while TRUE
' Handle async events without blocking:
' left/right adjust update time +/- 1 mSec
'
msg = port.GetMessage()
if type(msg)="roUniversalControlEvent" then
ecode = msg.GetInt()
if ecode = ecodes.BUTTON_RIGHT_PRESSED then
busy_ms = busy_ms + 1
else if ecode = ecodes.BUTTON_LEFT_PRESSED then
busy_ms = busy_ms - 1
end if
end if
' Sync and Swap the screen buffers.
' The timestamp should be just after vsync? - I thought!
'
screen.SwapBuffers()
current_frame_start = clk.TotalMilliseconds()
' Compute & display timings.
'
' The 1ms resolution measurements _should_ dither around
' the vsync time (16.6mSecs) or a multiple thereof.
' I.e. 16-17, 33-34 ...
'
if last_frame_start <> INVALID then
last_frame_ms = current_frame_start - last_frame_start
avg_ms = frame_avg_filter.update(last_frame_ms)
avg_us = int(1000 * avg_ms)
screen.clear(0)
x = txt_margin
y = txt_margin
screen.drawText("Animation: " + busy_ms.toStr() + " mSecs", x, y, txt_color, font)
y = y + txt_height
screen.drawText("Frame period: " + last_frame_ms.toStr() + " mSecs", x, y, txt_color, font)
y = y + txt_height
screen.drawText("Frame average: " + avg_us.toStr() + " uSecs", x, y, txt_color, font)
endif
' Bump the frame start history
'
last_frame_start = current_frame_start
' Simulate lengthy animation
'
while clk.TotalMilliseconds() < current_frame_start + busy_ms
' just spin
end while
end while
end sub
' Exponentially Weighted Moving Average (EWMA) filter
' Yt = Xt + α ( Yt-1 - Xt)
'
function jcEWMAFilter(alpha as Float, avg0 as Float) as Object
this = {}
this.alpha = alpha
this.avg = avg0
' Insert new sample, returns updated average.
'
this.update = function (x as Float) as Float
this = m
this.avg = x + (this.alpha * (this.avg - x))
return this.avg
end function
return this
end function
Have I got the wrong idea with SwapBuffers?
If so, how else can I sync?
Thanks.