computer speed independent programming

Share your advanced PureBasic knowledge/code with the community.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

computer speed independent programming

Post by blueznl »

Code updated For 5.20+

this might be of interest to people interested in games, it's more targetted towards beginners such as myself :-)

Code: Select all

; purebasic survival guide - pb3.93
; purebasic survival guide - pb3.93
; timer_1.pb - 03.03.2004 ejn (blueznl)
; http://www.xs4all.nl/~bluez/datatalk/pure1.htm
;
; - how to make games run at the same speed on different machines
; - GetTickCount_()
;
;
; GetTickCount_() returns a counter in milliseconds, since the system started
; it's limited to 49.7 days, so it wraps around after that...
; we've also got this 'signed/unsigned' thingy going on in purebasic...
;
; t1 = GetTickCount_()      ; there is however no guarantee that t2 is bigger than t1, as there
; t2 = GetTickCount_()      ; could be a wrap, or there could be a sign change
;
; to see how much time has passed, we could do:
;
; td = t2-t1
; if td > ...
;
; so we'd start at 0, count up to 2^31-1 = 2147483647, then from -2147483647 to -1, then 0 etc.
; but... at the moment of a sign change, t2 will be smaller than t1, and... at the moment of a wrap,
; td will be smaller than 0 (makes the brain hurt)
;
; an easy way around this is simply filter out unlikely values and live with it :-) so although we have
; 2 times a mistake during the 49.7 days, at least the program will not crash out or wait unneccessary long
;
; td = t2-t1
; if td < 0 or td > ...
;
w_main_nr = 1
w_main_h = OpenWindow(w_main_nr,0,0,400,400,"test",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
;
interval = 500000                                 ; interval frequency of 1 second
ticks = interval/1000                           ; regular counter has an accuracy of millisecs
;
Debug "low performance counter GetTickCount_()"
;
t1 = 0
t2 = 0
td = 0
;
Repeat
  event = WindowEvent()
  t2 = GetTickCount_()
  td = t2-t1
  If td < 0 Or td >= ticks                      ; check if enough time passed or a wrap took place
    t1 = t2
    ;
    n = n+1                           
    Debug Str(n)+" - "+Str(t2)+" - "+FormatDate("%SS",Date())
    ;
  Else
    Delay(20)                                   ; so we don't overload the cpu while doing nothing
  EndIf
Until event = 513 Or event = #PB_Event_CloseWindow
;
; GetTickCount_() does return a timer in milliseconds, if we would like to run at a clip of 60 frames
; per second, we would have to use an interval of 1000 / 60 = 16.6 = 17 ticks... hmm...
;
; there's a high performance counter in windows as well, unfortunately purebasic does not support
; 64 bits variables yet so we will have to do a little detour using floats (brrrr)
;
Debug ""
Debug "high performance counter"
;
hf_q.LARGE_INTEGER
ht_q.LARGE_INTEGER
ht1.f = 0
ht2.f = 0
htd.f = 0
;
If QueryPerformanceFrequency_(@hf_q) = 0
  Debug "no support"
EndIf
;
; every second it has hf_q.highpart * 2^32 + hf_q.lowpart ticks... that's a lot :-)
; i don't like floats, but without 64 bit support :-(
;
hf.f = hf_q\highpart*Pow(2,32)+hf_q\lowpart
hticks.f = hf*interval/1000000                  ; ticks on the high performance counter per interval
;
Repeat
  event = WindowEvent()
  QueryPerformanceCounter_(@ht_q)
  ht1 = ht_q\highpart*Pow(2,32)+ht_q\lowpart
  htd = ht1-ht2
  If htd < 0 Or htd >= hticks                   ; check if enough time passed or a wrap took place
    ht2 = ht1
    ;
    n = n+1                           
    Debug Str(n)+" - "+Str(t2)+" - "+FormatDate("%SS",Date())
    ;
  Else
    Delay(20)                                   ; so we don't overload the cpu while doing nothing
  EndIf
Until event = 513 Or event = #PB_Event_CloseWindow

Last edited by blueznl on Fri Mar 04, 2005 7:34 pm, edited 1 time in total.
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

we would have to use an interval of 1024 / 60 = 17 ticks
Actually, a millisecond is 1000 :)
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

ooooops

another previous language i worked in had a TIMER statement which worked in 1/1024 seconds... oooops again :oops:

well, at least someone noticed the post :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Check this site out!

http://www.bitbanksoftware.com/Programmers/code4.html

esp the part about
The 'high performance' functions allow you To directly Read The countdown timer Value.
The Value returned is a 64bit integer with The upper 48-bits being The system tick
counter And The lower 16 bits being The 1.19Mhz countdown timer Value.
I was wondering if just polling the lower 16bits would be enough?
then do a & $FFFF and return it as a long?.

0-65535 should give enough accuracy shouldn't it? (65536 ticks per second)
That would be um... 1092,2666666666666666666666666667 ticks per frame at 60fps.
and 655,36 ticks per frame at 100fps.
And at 256fps where you would only have 256 ticks per frame.
Altough the technical max would be with 1 tick per frame at 65536fps *laughs*

What do you think? May be able to avoid the use of that float, and thus speed up the code even more? Just doing a & $FFFF shouldn't have much overhead?

Edit: unless I missread the whole thing hmm.
In tests here it seems the "counter" does like 3 rounds per sec. *scratches head*
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

Thank you very much for that link Rescator.
BTW, i see you didn't got the explanation at all :?
And BTW too: what is the name of the needed functions in kernel32.dll ? There are no mention :o
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
LuCiFeR[SD]
666
666
Posts: 1033
Joined: Mon Sep 01, 2003 2:33 pm

Post by LuCiFeR[SD] »

one would assume they are refering to the api commands:

QueryPerformanceCounter & QueryPerformanceFrequency
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

LuCiFeR[SD] wrote:one would assume they are refering to the api commands:

QueryPerformanceCounter & QueryPerformanceFrequency
Not possible, because with:

Code: Select all

QueryPerformanceFrequency_(Freq.LARGE_INTEGER)
Debug Pow(2,32)*Freq\highpart+Freq\lowpart/1000000
I get 3.579545 (it is in MHz), and the Xtal frequency he is talking about is 1.1931817 MHz
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

i noticed that as well, it may be that there are alternative clock mechanisms in the system available

also what's bothering me is the 'downcount' reference, afaik it's counting 'up'

it stays interesting though...
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
MrMat
Enthusiast
Enthusiast
Posts: 762
Joined: Sun Sep 05, 2004 6:27 am
Location: England

Post by MrMat »

From http://msdn.microsoft.com/msdnmag/issue ... fault.aspx
On a different note, I was surprised at the results of QueryPerformanceFrequency under Windows 2000. Looking at Figure 2, notice that under Windows 2000 the timer operates at 3.57Mhz. This is exactly three times faster than the 1.19 Mhz frequency used on Windows 98. If you're a PC old-timer, you may recall that motherboards traditionally have a 1.19Mhz oscillator that drives the 8254 chip.
Intrigued, I ran my tests on a Pentium Pro 200Mhz running Windows 2000 and got the 1.19Mhz frequency. I then ran LoadTimer on some dual processor machines and found that the frequency matched the CPU speed. The conclusion I've drawn from this is that Windows 2000 observes what the motherboard is capable of doing and uses the best available timer frequency for the QueryPerformanceXXX APIs. Yes, I admit to being a nerd and spending way too much time on this minor detail, but I enjoyed the experimentation and the opportunity to ponder things at the hardware level.
On Win XP i get 2992 MHz (CPU speed) from QueryPerformanceFrequency.
Mat
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

MrMat wrote: On Win XP i get 2992 MHz (CPU speed) from QueryPerformanceFrequency.
:shock:
Are you saying that executing my tip above you get 2992 value :?: :!: :?:

Blueznl, neither i can't see the 16 bit countdown register. However, with a 64 bit register counting up is enough to perform stuff to avoid useless wasted CPU time for FlipBuffers() function, for example. :wink:
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
MrMat
Enthusiast
Enthusiast
Posts: 762
Joined: Sun Sep 05, 2004 6:27 am
Location: England

Post by MrMat »

Psychophanta wrote:Are you saying that executing my tip above you get 2992 value :?: :!: :?
No i get a -1302.217285 if i run your code but if i run
Debug StrU(Freq\lowpart,#Long)
then i get value 2992750000 (the upper part is 0) which is my CPU speed as that article says.
Mat
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

I see,
I didn't remember PB doesn't manage 64 bit numbers :o

Mmmm... then seems dual CPU PCs use 2nd CPU only for parallel timing processes :?
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

well, that does make sense, psycho, as single threads (running on a single cpu) are those that need this :-)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post by Psychophanta »

The solution i posted at viewtopic.php?t=12825 (the "Sat Dec 18, 2004 22:20" one) is in fact based on http://www.bitbanksoftware.com/Programmers/code4.html explanation.

I've added there an exact reproduction of http://www.bitbanksoftware.com/Programmers/code4.html explanation for PB. :)
http://www.zeitgeistmovie.com

while (world==business) world+=mafia;
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

So.. if I where to make say a 2D game (which I plan to do).


And I wanted the game main loop to do 100 or even 1000 loops per second,
for calculating and moving things etc etc.
but only redraw the graphics as often as the user has his refresh to.
How would one do that?

I.e. The user has say 85Hz set.
But you want you game to do 100 loops per second.

How the heck does one do that?
Is the first post in this thread still suitable for that or?

It's the need to use a lumpy winAPI system, and the odd 49.7 wrapping that
makes me see red, I can't believe ther isn't a simpler way.
A reliable way to count the passage of a second, regardless of platform.
1000ms accuracy would be perfect,
but heck even 100th of a second would be nice.

Some of the solutions I've seen seem odd or still tied to refresh rate.
Remember 1000ms/60Hz would be 16,666666666666666666666666666667
That is like 16 and a half "loops" possible per second.

I want the user to be able to set whatever refresh rate he/she want.
(i.e a refresh rate independent game)
because to me the refresh rate is just how often a game/program
should update the display (if neccessary, i.e stuff has changed visualy)
I want to have that part completly seperate from the game speed.

The first post in this thread is darn interesting in that respect tough.

The reason I wish to fully seperate the display rate and game speed
is because, if a computer is darn slow, no matter what refresh rate is used
the graphics will still "lag". And if the computer is superfast,
well, the graphics will definetly not "lag".

I was toying with one possible way,
by using timers, but as has become obvious the timers can be either innacurate when higher resolutions are needed,
or may get "lost" in the system queue.

So, how do I go about making a game engine timers/loop that
occurs 100 times per second.
and the graphics are updated only if there is actual changes.
Minimum data update rate would be 100th of a second,
maximum would be, well possibly unknown in case the player don't move at all if you know what I mean.
The refresh rate in my eyes is a graphics engine thing only,
and I'd love to be able to separate all graphics timing from game timing.

I.e the game nudges the graphics routine saying "here's some new data to draw" and the graphics engine go "ok, I'll do it on my next refresh".
In the mean time the game does other things.

I'm a noob so please have patience with me :P
Post Reply