## What is good cross platform gameloop?

Game development specific discussions.
dodicat
Posts: 6628
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: What is good cross platform gameloop?

A very easy and cpu friendly way of setting a framerate is:

Code: Select all

`Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long    Static As Double timervalue,_lastsleeptime,t3,frames    frames+=1    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000    If sleeptime<1 Then sleeptime=1    _lastsleeptime=sleeptime    timervalue=Timer    Return sleeptimeEnd Functionsub Pendulum(x as long,y as long,angle as double,length as double,col as ulong)    angle=angle*.0174532925199433     var x2=x+length*cos(angle)    var y2=y-length*sin(angle)     line(x,y)-(x2,y2),col    circle(x2,y2),50,col,,,,f end subscreen 19dim as long fpsdim as long requiredframerate=60 '<---   SET YOUR RATE HERE dim as single angdo    ang+=.05    screenlock    cls    Pendulum(400,20,45*sin(ang)-90,400,4)'< --- simple pendulum to demonstrate the framerate     draw string(50,50),"Framerate "&fps    screenunlock    sleep regulate(requiredframerate,fps)    loop until len(inkey)         `
coderJeff
Posts: 3258
Joined: Nov 04, 2005 14:23
Contact:

### Re: What is good cross platform gameloop?

dafhi, interesting: a separate frame rate for physics and animation. Years ago, I tried writing a rigid body physics solver and had trouble, each for different reasons, with both fixed and variable frame rates for then. Do the choices of 1/67, 1/57, rate = pi*..., have special meaning? And I suspect if physics calculations take too long, the anim_t will never go above zero.

dodicat, thanks, nice, I can run that. :) Yeah, I think I need to decide what to do when the program can't keep up. For example, if some system task suddenly starts, it will interfere with the timing. Otherwise, your example delivers on the fixed frame rate.

Couple more things:
1) You both suggest screenlock/screenunlock only. I would tend to use page flipping, not for any good reason, just my habit to do so. I will try screenlock/screenunlock method.
2) Neither of you suggest a screensync. Without screensync, I see tearing on win7, both windowed and fullscreen. I suppose that could be a user configurable option.
3) If screensync is used, then physics timing should probably match monitor refresh rate. Or if monitor rate is too high for physics to keep up, then some even multiple of each other.

Thanks for the suggestions, it is helping me get closer to an answer.
Imortis
Moderator
Posts: 1725
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

### Re: What is good cross platform gameloop?

coderJeff wrote:...
1) You both suggest screenlock/screenunlock only. I would tend to use page flipping, not for any good reason, just my habit to do so. I will try screenlock/screenunlock method.
2) Neither of you suggest a screensync. Without screensync, I see tearing on win7, both windowed and fullscreen. I suppose that could be a user configurable option.
...

Screenlock/screenunlock, if used properly will eliminate tearing. I have an example game engine with a gameloop that uses this method without issue. The code does not compile nicely on newer versions of the compiler, but that is because I am using an older version of fbSound. I should really update that to the newest version so it can work again.

EDIT:

In SpaceGame.bas in the GameCycle() Sub You will see that I call the GamePaint() between screenlock and screenunlock. As much as possible, you want to ONLY do screen access between those commands. Anything else could result in flicker and if you put any input in there the result looks like the program has frozen.
dafhi
Posts: 1351
Joined: Jun 04, 2005 9:51

### Re: What is good cross platform gameloop?

coderJeff wrote:dafhi, interesting: a separate frame rate for physics and animation. Years ago, I tried writing a rigid body physics solver and had trouble, each for different reasons, with both fixed and variable frame rates for then. Do the choices of 1/67, 1/57, rate = pi*..., have special meaning?

[rate = some_value * frames * iphys_fps] would have been better. my bad.

so 60 is normal refresh. I like to throw in off-values for dithering. Sometimes perfect refresh alignments will cause a regular stutter if, say a render took too long and things are catching up the next frame. Another way I can think to describe it is, 120 for physics and 60 refresh is too digital

> And I suspect if physics calculations take too long, the anim_t will never go above zero.

nice. fixed it with a loop

i've experienced stuttering with page flip. So i use lock
coderJeff
Posts: 3258
Joined: Nov 04, 2005 14:23
Contact:

### Re: What is good cross platform gameloop?

Just read this topic by fxm. Covers the basics that would lead in to this topic discussion. Placing a link here for reference.
How to Solve the Flickering Issue when Refreshing a Window in FB Graphics Mode
coderJeff
Posts: 3258
Joined: Nov 04, 2005 14:23
Contact:

### Re: What is good cross platform gameloop?

Still messing with this, thanks for the input and ideas...

My new thoughts on frame rates:
- fastest animation rate is limited by the refresh rate of the monitor. A display fps (frames per second) higher than the monitor refresh rate is meaningless, pointless, and just wasting CPU drawing frames that will never be seen
- a faster physics rate, e.g. double or triple the monitor refresh rate can be good, as it can smooth out movements and jitters/stutters/jumps, whatever you want them.

Demo:
Using some ideas from fxm's articles, I am continuing to work on this demo for testing; can run it, but not finished yet:
fbgfx-game-loop-demo-test.bas

Has menus to select the screen mode size, bit depth, change to full screen, etc, all while running the program; don't need to exit or recompile. At least that's the goal. I think some parts of the code turned out really nice, other parts just to get it working. No doubt I will get berated for the use of dim shared, again. I don't care. It's working as I thought so far. For what it does, I think my fake VB text mode listbox turned out decent.

Just one source file, no extra files needed makes it easy to copy/paste/run. Otherwise, getting to the point I want to split it up in to modules.
dafhi
Posts: 1351
Joined: Jun 04, 2005 9:51

### Re: What is good cross platform gameloop?

super-excited! Haven't looked at the code too much but it's going in the vault
dodicat
Posts: 6628
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: What is good cross platform gameloop?

I had to set up manually

lines 1258 ish
dim as integer opt_width = 1024
dim as integer opt_height =768'
dim as integer opt_depth = 32

Otherwise I got
"invalid screen mode "
from
line 1322
print "invalid screen mode " & opt_width & "x" & opt_height & "x" & opt_depth
sleep <----- You didn't put in sleep.

For 64 bit to work
line 347: declare property ItemData( byval index as integer, byref value as integer ) '<----INTEGER
and
line 424: property UI_LISTBOX.ItemData( byval index as integer, byref value as integer ) '<------INTEGER

win 10
fb 1.05.0

Refresh rate here is 60.
NOTE: the 64 bit compiler is jumpy here.
MrSwiss
Posts: 3569
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: What is good cross platform gameloop?

[deleted duplicate]
Last edited by MrSwiss on Jun 12, 2018 19:21, edited 1 time in total.
MrSwiss
Posts: 3569
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: What is good cross platform gameloop?

dim as integer opt_width = 1920
dim as integer opt_height = 1080
dim as integer opt_depth = 32
had to be done (FBC 64, 1.05/1.06), as dodicat describes ...
multiple Sleep statements (before each End x, statement)
recoded SCREEN_MODE_SIZE:

Code: Select all

`' since field alignments are useless (doesn't change anything), as well as the Type wrapper ...Union SCREEN_MODE_SIZE  ' since we can 'name' the Union (FBC versions: 1.05/1.06, 32/64 WIN)    mode as ULong   ' corrected, was Long (2 x UShort = ULong), the sign bit is unwanted, here!   Type      h as ushort ' low 2 ubytes[0, 1] (little endian systems)      w as ushort ' high 2 ubytes[2, 3]   end typeend Union`
as well as:
replaced all Val() statements, with CInt() statements (we want 'Integer', not 'Double & implicit conversion')

findings:
colors: 8/15/16 (initial), give a compile, but .exe run's "hidden" (as a service would)
colors: 24/32 (initial) are OK
best experience setting:
- double buffer (menu setting) only 32 bit depth tested (as below)
- compiler and switches: FBC 64 WIN with: -s gui -gen gcc -O 3 ver. 1.06 (current)
- best (most stable) fps output: FBC 64 (FBC 32, is really 'bad news' here, changing rather wildly)

smallest .exe size (64/1.06): 171 KB
size difference 32/64 (1.05), none if -gcc -O 3 used: 188 KB
dodicat
Posts: 6628
Joined: Jan 10, 2006 20:30
Location: Scotland

### Re: What is good cross platform gameloop?

Here is my simple idea.
1) fix a frame rate via a regulator, and keep it at that value throughout.
or
2) Enable various frame rates, but keep the screen motion constant.

The events here are driven from an increment of a float at each frame (arbitary)
My example shows two identical pendulums (subs), one driven from the (delta) increment directly, the other from a function of the (delta) increment.
The frame rates can be changed via the slider.

Code: Select all

` Function Regulate(Byval MyFps As Long,Byref fps As Long) As Long    Static As Double timervalue,_lastsleeptime,t3,frames    frames+=1    If (Timer-t3)>=1 Then t3=Timer:fps=frames:frames=0    Var sleeptime=_lastsleeptime+((1/myfps)-Timer+timervalue)*1000    If sleeptime<1 Then sleeptime=1    _lastsleeptime=sleeptime    timervalue=Timer    Return sleeptimeEnd Function'identical pendulumsSub Pendulum1(x As Long,y As Long,angle As Double,length As Long,col As Ulong,Byref p As Single)    Static As Double t    Static As boolean flag     dim as single minx=x-length*sin(30*.0174532925199433)+.5     dim as single maxx=x+length*sin(30*.0174532925199433)-.5    angle=angle*.0174532925199433     Var x2=x+length*Cos(angle)    If x2>maxx Then flag=true    If x2<minx Then         If flag=true Then p=Timer-t:t=Timer        flag=false    End If    Var y2=y-length*Sin(angle)    Line(x,y)-(x2,y2),col    Circle(x2,y2),50,col,,,,f End SubSub Pendulum2(x As Long,y As Long,angle As Double,length As Long,col As Ulong,Byref p As Single)    Static As Double t    Static As boolean flag    dim as single minx=x-length*sin(30*.0174532925199433)+.5    dim as single maxx=x+length*sin(30*.0174532925199433)-.5    angle=angle*.0174532925199433     Var x2=x+length*Cos(angle)    If x2>maxx Then flag=true    If x2<minx Then         If flag=true Then p=Timer-t:t=Timer        flag=false    End If    Var y2=y-length*Sin(angle)    Line(x,y)-(x2,y2),col    Circle(x2,y2),50,col,,,,f End Sub#define map(a,b,x,c,d) ((d)-(c))*((x)-(a))/((b)-(a))+(c)#macro displayang1+=delta   'delta =.0006*refreshrate (arbitary speed)     'fps depentent pendulumang2+=refreshrate*delta /requiredframerate                   'fps independent pendulumScreenlockCls'draw pendulumsPendulum1(300,20,30*Sin(ang1)-90,400,Rgb(0,200,0),p1)    'period varies with fpsPendulum2(700,20,30*Sin(ang2)-90,400,Rgb(0,100,200),p2)  'period constantDraw String(350,700),"Move red circle to alter the framerate",Rgb(200,200,200)Draw String(50,20),"Monitor refresh rate = " &refreshrateDraw String(50,50), "Requested Framerate "&requiredframerateDraw String(450,500),"fps independent Period = " &p2,Rgb(0,100,200)Draw String(050,500),"fps dependent Period = " &p1,Rgb(0,200,000)Draw String(50,70),"Actual    Framerate "&fpsdraw string(50,120),"Right click mouse to reset fps to monitor refresh rate"draw string(50,150),"Please wait for a few swings to calculate the periods",rgb(100,100,100)'slider circle and boxdraw string(280,650),"34"draw string(710,650),"156"Line(300,650)-(700,680),Rgb(0,100,255),bfCircle(circx,665),12,Rgb(200,0,0),,,,fScreenunlockSleep regulate(requiredframerate,fps)'fps regulator#endmacro#macro mouseDim As Long x=mx,y=my,ddx,ddyWhile mb = 1    Display    Getmouse mx,my,,mb    If mx<>x Or my<>y  Then        ddx = mx - x        ddy = my - y        x = mx        y = my        circx=x+ddx        If circx<312 Then circx=312        If circx>688 Then circx=688        requiredframerate=map(300,700,circx,30,160)      End IfWend#endmacro#define incircle(cx,cy,radius,x,y) (cx-x)*(cx-x) +(cy-y)*(cy-y)<= radius*radiusScreen 20,32Const circy=665 'y position of slider circleDim As Long mx,my,mb,circxDim As integer refreshrateScreeninfo ,,,,,refreshrateDim As Long fps=refreshrateDim As Long requiredframerate=refreshrate circx=map(30,160,requiredframerate,300,700)  'first position of red circleDim As Single ang1,ang2                      'angular input for each pendulumDim As Single p1,p2                          'periods for each pendulumdim as single delta=.0006*refreshrate       'chosen speed increment (arbitary)Do    Getmouse mx,my,,mb    If mb=2 Then requiredframerate=refreshrate:circx=map(30,160,requiredframerate,300,700) 'reset    display    If incircle(circx,circy,10,mx,my) And mb=1 Then: mouse:End If                          'sliderLoop Until Len(Inkey)sleep  `
coderJeff
Posts: 3258
Joined: Nov 04, 2005 14:23
Contact:

### Re: What is good cross platform gameloop?

Thanks for testing guys. I should have tested in 64-bit first. I posted new updates to the gist.

Intent was to provide a demo that just runs; you shouldn't have to modify the source to get it running. Looks like I need to address that first.

@dodicat,
- The 64-bit is jumpy here too, even in full screen mode.
- I am planning to add selection of different timing modes, if OK with you, I was planning to add your pendulum code, I like it. I want to be able to select between a few different screen drawing demos.

For me, smoothest action on win7 so far is, 32-bit exe, fullscreen, any size, any depth, using page flipping.

ENUM and PROPERTY as LONG/INTEGER
- I see, ENUM has sizeof(integer), and can't assign ENUM type to PROPERTY of LONG type.
- I don't think that's a bug, though should probably investigate more if that is more related to ENUM or PROPERTY.

Default Screen Mode:
- I thought 1024x768x16 would be a safe choice for default mode. I guess not.
- I changed how the default mode is selected...
- default mode is automatically selected to be largest screen mode that is also smaller than desktop.
- I am trusting that gfxlib2's ScreenList() provides good results, if it's not, then we have other problems.
- Also, I changed the dim shared modelist*() arrays to an one object that should choose a working default SCREENRES mode every time

SLEEP at end of a program:
- I don't agree with adding SLEEP to every program - this program is supposed to be a console program (no -s gui)
- HOWEVER, I am guessing you are running from IDE or Explorer, so I added some #ifdef __FB_WIN32__ specific code:
A) if run from Explorer or an IDE (that doesn't capture console output), program should SLEEP and wait for key press.
B) if run from Cmd, program should just end
- Basically it checks if the program created the console, or the parent process created the console

Code: Select all

`#ifdef __FB_WIN32__   #include once "windows.bi"   '' if not running from command line, add SLEEP statment   sub MaybeSleep()      dim as HWND consoleWnd = GetConsoleWindow()      dim as DWORD dwProcessId      GetWindowThreadProcessId(consoleWnd, @dwProcessId)      if( GetCurrentProcessId() = dwProcessId ) then         locate 1, 1         hColor 15, 0         print "PRESS ANY KEY TO EXIT ...     "         sleep      end if   end sub#else   #define MaybeSleep()#endif`

Recoding of SCREEN_MODE_SIZE:
@MrSwiss,
- I didn't know that FIELD=1 makes no difference. I want to ensure the structure is packed, so it seems like a good idea to specify it.
- ScreenList() returns LONG, however, H & W are DWORD on windows and __u32 on linux (i.e. USHORT) at the API, so, it's messy...
- I see your point about little vs big endian. Maybe should be using HIWORD/LOWORD instead of a structure.

Code: Select all

`union SCREEN_MODE_SIZE   mode as long   type field = 1#ifdef __FB_BIGENDIAN__      w as ushort '' width - hiword      h as ushort '' height - loword#else      h as ushort '' height - loword      w as ushort '' width - hiword#endif   end typeend uniontype SCREEN_MODE extends SCREEN_MODE_SIZE   d as long '' depthend type`

I can't address all issues today; or make all the changes I want to today. I will keep posting.

Thanks again for the responses.

@dodicat, I like the demo with 2 pendulums (pendula?). Yeah, it can be confusing, because with a monitor refresh rate, 60hz for me, I will see a new image every 16.67ms. But the "fps" simulated can be slower or faster than this. The comparison, ON ONE SCREEN, really helps. Cool.
MrSwiss
Posts: 3569
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: What is good cross platform gameloop?

@coderJeff,
coderJeff wrote:H & W are DWORD on windows and __u32 on linux (i.e. ULONG SHORT) at the API
Aka: H = WORD, L = WORD (USHORT) ... by M\$'s definition of data-types, as well as __u32 seems to be ...
(I'm not 'a jour' on LIN-vars.).

the Field=1 statement in -lang FB has only an effect if:
1) the default packing (by compiler) is padding, to achieve allignment (not the case, in the Union)
2) if a type by its internal structure, requires padding, e.g. a long + a byte (padding 3 bytes, to SizeOf(long))
3) by cons, using byte-size allignment, may slow down arrays (of type), because of bad memory allignment
4) padding is always based on the largest numeric variable present:
a) any sort of Pointer (also in case of String, since header has a Pointer & Integer's)
b) largest sized numeric Variable
Especially 3) is the one, to consider. I prefer to add padding manually, instead of Field, to preserve allignment as
in Ex. 2 below (some bytes labelled as 'reserved' or 'res''). I'll take the "speed" first ... the few saved bytes, far later
(in this particular case only!). I'm saving much more bytes, by NOT using Integer (Short/Long instead)! Mainly used:
FBC 64 (where: Integer = 64 bit type), which may explain it somewhat better (less impact on FBC 32, of course).

Example 1 "alligned by variables type choice" (all same size = alligned automatically):

Code: Select all

`Type Average    ' variables and methods for average calculation  Private:      ' variables are: protected from direct access    As Double   sum                     ' 64 bit type    As Double   lval                    ' 64 bit type    As ULongInt cnt                     ' 64 bit type  Public:       ' accessible from 'outside' to: manipulate type     ...End Type`

Example 2 "manually alligned by additional variable":

Code: Select all

`Type Average    ' variables and methods for average calculation  Private:      ' variables are: protected from direct access    As Double   sum                     ' 64 bit type    As Double   lval                    ' 64 bit type    As ULong    cnt                     ' 32 bit type    As UByte    res(0 to 3)             ' 4 x 8 = 32 (allignent again)   Public:       ' accessible from 'outside' to: manipulate type     ...End Type`
coderJeff
Posts: 3258
Joined: Nov 04, 2005 14:23
Contact:

### Re: What is good cross platform gameloop?

MrSwiss wrote:@coderJeff,
coderJeff wrote:H & W are DWORD on windows and __u32 on linux (i.e. ULONG SHORT) at the API
Aka: H = WORD, L = WORD (USHORT) ... by M\$'s definition of data-types

On windows, dmPelsWidth & dmPelsHeight returned by DEVMODE structure are DWORD. SCREENINFO packs the LOWORD of each in to a FB LONG data type. Similar on linux with __u32 = unsigned 32-bit integer type. I understand what you are trying to show, but I'm not in control of the data, gfxlib is, and union of LONG and 2 packed USHORTS is exactly what gfxlib is doing.
MrSwiss
Posts: 3569
Joined: Jun 02, 2013 9:27
Location: Switzerland

### Re: What is good cross platform gameloop?

I don't quite understand why the return is Long, but:

Code: Select all

`Dim As ULong    mode, cnt = 1Dim As UShort   w, h, cd = 32   ' cd = color depth (in bit's)' macros: v (value) is converted to ULong (32 bit) then filtered (to 16 bit)' since 'built in' macros cast to UInteger, they're considered "unsafe" in FBC 64#Define Hi16(v)    ( CULng(v) Shr 16 )          ' get High 16 bit's#Define Lo16(v)    ( CULng(v) And &h0000FFFF )  ' get Low 16 bit'sPrint "Resolutions supported at "; cd; " bits per pixel:"mode = ScreenList(cd)           ' get first 'mode' (depth param. required)While mode                      ' run until mode = 0    w = Hi16(mode)              ' macro call (see above)    h = Lo16(mode)              ' dito    Print "mode"; cnt; Tab(10); w; "x"; h    cnt += 1    mode = ScreenList()         ' try, to get next 'mode'Wend : cnt -= 1                 ' decrement counter (last = nothing)Print : Print "ScreenModes ("; cd; " bit): "; cntSleep`
works, like a charm ...

DWORD = __u32 = unsigned 32-bit integer type = ULong in FB
GFXlib2 has the BUG, here! aka: returns the wrong data-type!
(but as long, as we have comparatively small screen dimensions
in pixels, it can simply be put [without problems], to ULong)