What is good cross platform gameloop?

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

Re: What is good cross platform gameloop?

Post by dodicat »

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 sleeptime
End Function

sub 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 sub


screen 19
dim as long fps
dim as long requiredframerate=60 '<---   SET YOUR RATE HERE 
dim as single ang
do
    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
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

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: 1923
Joined: Jun 02, 2005 15:10
Location: USA
Contact:

Re: What is good cross platform gameloop?

Post by Imortis »

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:
Here is a download link to the old version:
Download here

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: 1640
Joined: Jun 04, 2005 9:51

Re: What is good cross platform gameloop?

Post by dafhi »

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
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

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
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

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.

Image

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: 1640
Joined: Jun 04, 2005 9:51

Re: What is good cross platform gameloop?

Post by dafhi »

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

Re: What is good cross platform gameloop?

Post by dodicat »

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: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: What is good cross platform gameloop?

Post by MrSwiss »

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

Re: What is good cross platform gameloop?

Post by MrSwiss »

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 type
end 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: 7976
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: What is good cross platform gameloop?

Post by dodicat »

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 sleeptime
End Function

'identical pendulums
Sub 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 Sub

Sub 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 display

ang1+=delta   'delta =.0006*refreshrate (arbitary speed)     'fps depentent pendulum
ang2+=refreshrate*delta /requiredframerate                   'fps independent pendulum
Screenlock
Cls
'draw pendulums
Pendulum1(300,20,30*Sin(ang1)-90,400,Rgb(0,200,0),p1)    'period varies with fps
Pendulum2(700,20,30*Sin(ang2)-90,400,Rgb(0,100,200),p2)  'period constant

Draw String(350,700),"Move red circle to alter the framerate",Rgb(200,200,200)
Draw String(50,20),"Monitor refresh rate = " &refreshrate
Draw String(50,50), "Requested Framerate "&requiredframerate
Draw 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 "&fps
draw 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 box
draw string(280,650),"34"
draw string(710,650),"156"
Line(300,650)-(700,680),Rgb(0,100,255),bf
Circle(circx,665),12,Rgb(200,0,0),,,,f
Screenunlock
Sleep regulate(requiredframerate,fps)'fps regulator
#endmacro

#macro mouse
Dim As Long x=mx,y=my,ddx,ddy
While 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 If
Wend
#endmacro

#define incircle(cx,cy,radius,x,y) (cx-x)*(cx-x) +(cy-y)*(cy-y)<= radius*radius

Screen 20,32
Const circy=665 'y position of slider circle
Dim As Long mx,my,mb,circx
Dim As integer refreshrate
Screeninfo ,,,,,refreshrate
Dim As Long fps=refreshrate
Dim As Long requiredframerate=refreshrate 
circx=map(30,160,requiredframerate,300,700)  'first position of red circle
Dim As Single ang1,ang2                      'angular input for each pendulum
Dim As Single p1,p2                          'periods for each pendulum

dim 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                          'slider
Loop Until Len(Inkey)
sleep

  
coderJeff
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

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 type
end union

type SCREEN_MODE extends SCREEN_MODE_SIZE
	d as long '' depth
end 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: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: What is good cross platform gameloop?

Post by MrSwiss »

@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
Site Admin
Posts: 4313
Joined: Nov 04, 2005 14:23
Location: Ontario, Canada
Contact:

Re: What is good cross platform gameloop?

Post by coderJeff »

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: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: What is good cross platform gameloop?

Post by MrSwiss »

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

Code: Select all

Dim As ULong    mode, cnt = 1
Dim 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's

Print "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): "; cnt

Sleep
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)
Post Reply