FPS Counter
Lachlan87Yes, a very simple concept, but I've seen so many different ways of doing it, I want to double check and make sure the way I am doing it is acceptable. If I run this code everytime I draw everthing to the screen, I should get accurate FPS, right? [code] numFrames += 1 If timeGetTime - FirstTime >= 1000 Then FPS = numFrames FirstTime = timeGetTime numFrames = 0 End If [/code] Really, I feel silly asking this question since I understand the concept just fine. But after I tried implementing someone elses version of a FPS counter, and it didn't work, I'm not sure I trust myself to have got it right this time around.[:p]
Sr. GuapoThat is one way of doing it (and how I do it). You could use QueryPerformanceCounter for more accuracy, but it really isn't needed. That should work fine.
EACamJust to be picky...i like to place the FirstTime = timeGetTime statement LAST because it would supposedly be more accurate that way...but then again, the 2 lines before it would barely take a phemtosecond, so it's no big deal...or even a little deal. [:D]
Lachlan87Ok, thanks. I have one more question though. Why does useing GetTickCount make my FPS so much lower than using TimeGetTime? TimeGetTime will put me at about 85 FPS, whereas GetTickCount will put me at about 20 FPS.
EACamUm...what IS TimeGetTime? I've never heard of it.
Sr. GuapoI've seen it, but I always though it was the same as GetTickCount... Guess not...
Eric ColemanThat's odd, there shouldn't be that much of a difference between the two functions.
masterboodaJust to add my 2cents to this post, Query Perfomance is a lot better to use when you are wanting to manage a frame rate, for example I used the above method and my program starts at 400, but I want it to run to 60fps... It took a long time to go down to 60fps(because the program has to find the best delay)... but with query I can put a delay into the loop that will only allow it to draw 60fps... Of course this is just my opinion... for even better accuracy use timed movement, and then you don't have to worry about fps at all... DaBooda out... P.S. If all you are doing is reading the FPS the above method works fine...
Sr. GuapoYes, QPC (for short) is much better for setting a framerate and doing things like time based modelling, but for only calculating the FPS, getTickCount/timeGetTime should be more than enough...
Sr. GuapoAlso, have you checked your various processes and CPU usage with Task Manager? It is possible that GetTickCount is using a different (and more congested) thread than timeGetTime... If you are still having problems with either of those, just switch to QPC. It is very easy to do, and it never has any problems that I've heard of...
game_makerwill if you agree that there is no game should run with speed of < 5 then put a condition if fps < 5 then fps = 60 (to manage the first second) another thing the comparing (>=) may do some further calculation than > so I always like to use x > (y-1) insteed of x >= y ,,,ie > 999 for integers [:)]
EACamReally? Does that speed it up? I've never heard of it, but doing > (y - 1) seems that it COULD be faster, i wouldn't know.
VBBRTo me it seems slower, because first it needs to do the calculation (y - 1) and then compare the values. (oh and just for you all to know, I just made my first subscription to pscode [:D], if you want to take a look at it, see [url="http://www.pscode.com/vb/scripts/ShowCode.asp?txtCodeId=54318&lngWId=1&txtForceRefresh=61120041193538923"]here[/url].
game_makerVBBR : (y - 1) is pre-calculation. I.e. you don't put 1000-1, you just put 999 Still not that sure There is 2 way to solve (>=) It may be separate (meaning first compare > then compare = ) I.e. x >= y change to (x > y or x = y) For this, clearly x > (y-1) faster, where (y-1) is pre-calculated Or combining method (test >= together): witch I don't know [8)] I don't know what vb is using [8)] but doing x > (y-1) seems to got more logic percentage [?]
VBBRBut it will be slower if (y - 1) can't be pre-calculated i.e. a varible-sized array loop.
game_makerunquestionably [:)]
Lachlan87timeGetTime seems to work fine, but maybe I'll switch to QueryPerformanceCounter anyway, if it's that much faster. What is really strange to me is how much of a difference there is between GetTickCount and timeGetTime. I'm only drawing a handful of images, and I have a pretty powerful computer by many peoples standards (the only reason timeGetTime gives me 85 is because that's what my monitors refresh rate is set at). It seems to me even if it was a congested thread, it wouldn't make that much of a difference. The cpu usage is the same no matter which I use. . . maybe I'll try it on a different computer and see what happens.
Eric ColemanIf "GetTickCount" returns "ticks", what exactly are the time units of a single tick? It might be that a "tick" is just an incrimented number whenever the system timer is updated. The discrepency that you experienced is just really odd, and I can't think of any other way to explain it. The precision of timeGetTime is on the order of 10 ms, which is 1/100th of a second. Considering that a game running at 60 FPS must complete a single frame in 1/60th of a second, then for timing purposes (animations and stuff) timeGetTime isn't exactly the best thing to use because the precision of the time step is relatively small compared with the size of the frame time step.
Lachlan87According to MSDN, GetTickCount's "return value is the number of milliseconds that have elapsed since the system was started." Sounds the same as timeGetTime to me. . . Tested it on my brother's computer, and had the same result: 85 FPS for timeGetTime, 20 FPS for GetTickCount. Has anyone else noticed this with their games?
Eric ColemanIs the FPS value when using GetTickCount wrong or does it slow down the game to 20 FPS? I only ever used QueryPerformanceCounter or timeGetTime, so I never noticed the getTickCount problem.
game_makerI have just tested this code [code] Option Explicit Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function GetTickCount Lib "kernel32" () As Long Dim numFrames1 As Long, FirstTime1 As Long, FPS1 As Long Dim numFrames2 As Long, FirstTime2 As Long, FPS2 As Long Private Sub Form_Load() Me.Show Do numFrames1 = numFrames1 + 1 If timeGetTime - FirstTime1 > 999 Then FPS1 = numFrames1 FirstTime1 = timeGetTime numFrames1 = 0 End If numFrames2 = numFrames2 + 1 If Sgn(GetTickCount - FirstTime2 - 999) + 1 Then FPS2 = numFrames2 FirstTime2 = GetTickCount numFrames2 = 0 End If DoEvents Me.Caption = "FPS1 = " & FPS1 & " " & "FPS2 = " & FPS2 DoEvents Loop End Sub [/code] they are 100% same [:)] ,, i.e. as Eric said your test is odd [8)]
Eric ColemanHere is some more code. Just copy and paste this on a form named "form1" with a command button named "command1" This code tests the resolution of the different timers. It calls the time function, and then repeatedly calls the time function untill the returned value changes. The difference between the two values shows the precision of the timer function. The Timer function seems to be slightly more precise, but less accurate on my system. It is more prone to fluctuations returning either it's time step or it's time step times 2. [code] Private Declare Function GetTickCount Lib "kernel32" () As Long Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function QueryPerformanceFrequency Lib "kernel32" (lpFrequency As Currency) As Long Private Declare Function QueryPerformanceCounter Lib "kernel32" (lpPerformanceCount As Currency) As Long Private Sub Command1_Click() Form1.Cls Form1.CurrentX = 0: Form1.CurrentY = 0 Dim s As Long, t As Long Dim u As Single, v As Single Dim Freq As Currency, x As Currency, y As Currency, sngResult As Single s = GetTickCount() Do t = GetTickCount() If t - s > 0 Then Exit Do Loop Form1.Print (t - s) / 1000 'convert from milliseconds to seconds s = timeGetTime() Do t = timeGetTime() If t - s > 0 Then Exit Do Loop Form1.Print (t - s) / 1000 'convert from milliseconds to seconds. u = Timer Do v = Timer If v - u > 0 Then Exit Do Loop Form1.Print (v - u) 'already in seconds. QueryPerformanceFrequency Freq QueryPerformanceCounter x Do QueryPerformanceCounter y If y - x > 0 Then Exit Do Loop sngResult = (y - x) / Freq Form1.Print Format$(sngResult, " 0.000000000000") End Sub [/code] after writing this I found the following from microsoft, it seems they did the same thing I just did, http://support.microsoft.com/default.aspx?scid=http://support.microsoft.com:80/support/kb/articles/Q172/3/38.asp&NoWebContent=1
Lachlan87One thing I forgot to mention, and may be key to my problem, is that I am using VB.Net, which doesn't always get along with the windows API so well. game_maker's code gives me about 1187 for both values when I put it in VB6--but when I convert it to VB.Net like so: [code] Private Declare Auto Function timeGetTime Lib "winmm.dll" () As Long Private Declare Auto Function GetTickCount Lib "kernel32" () As Long Dim numFrames1 As Long, FirstTime1 As Long, FPS1 As Long Dim numFrames2 As Long, FirstTime2 As Long, FPS2 As Long Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load Me.Show() Do numFrames1 = numFrames1 + 1 If timeGetTime - FirstTime1 > 999 Then FPS1 = numFrames1 FirstTime1 = timeGetTime numFrames1 = 0 End If numFrames2 = numFrames2 + 1 If Math.Sign(GetTickCount - FirstTime2 - 999) + 1 Then FPS2 = numFrames2 FirstTime2 = GetTickCount numFrames2 = 0 End If Application.DoEvents() Me.Text = "FPS1 = " & FPS1 & " " & "FPS2 = " & FPS2 Application.DoEvents() Loop End Sub [/code] I get about 85203 for timeGetTime and about 21843 for GetTickCount. It doesn't make any sense to me. I would instinctively guess that VB.Net wasn't converting the data types correctly, but since both timeGetTime and GetTickCount return a Long, it ought to make the same error on both, if it truely was a conversion error. Besides, VB.Net's long can hold much greater values that VB6's, so it seems like there would be no problem. But that was my reasoning using logic, which doesn't apply when we're working with a product made by Microsoft. I changed GetTickCount to As Integer, and Voila! Now they both return 85 thousand. For some perverted reason GetTickCount wouldn't convert right and timeGetTime would. But that still leaves the question: Why are the values I get in VB.Net so wildy different than the values I get in VB6? I can't decide if VB.Net is too high, or if VB6 is too low, but they can't both be right! Can they? When logic as I understand it fails, I start to wonder . . . And just to confuse things a little more, Eric's VB6 code returns the same values for timeGetTime and GetTickCount as my VB.Net verison of his code---without changing any longs to integers!!
Sr. GuapoIt is possible that DoEvents in VB6 doesn't take as long as Application.DoEvents() in VB.NET... IT doesn't seem likely, but I don't know what else it would be...
Lachlan87Ok, I think I finally figured it out. First, changing the title bar's text makes for quite a speed hit, so I removed that. That made both versions much faster, but vb6 was still lagging behind by about 100,000. So I did a rather sloppy test(Hopefully Almar will do a better one for persistent realities), and it seems as though vb.net can call timeGetTime and GetTickCount faster than VB6. My very unprofessional testing code is below. VB6 Version: [code] Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function GetTickCount Lib "kernel32" () As Long Private Sub Command1_Click() Dim firstTime As Long Dim frames As Long frames = 0 firstTime = GetTickCount Do Until GetTickCount - firstTime > 999 frames = frames + 1 Loop Form1.Print frames frames = 0 firstTime = timeGetTime Do Until timeGetTime - firstTime > 999 frames = frames + 1 Loop Form1.Print frames End Sub [/code] VB.Net Version: [code] Private Declare Auto Function timeGetTime Lib "winmm.dll" () As Long Private Declare Auto Function GetTickCount Lib "kernel32" () As Integer Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim firstTime As Long Dim frames As Long frames = 0 firstTime = GetTickCount Do Until GetTickCount - firstTime > 999 frames = frames + 1 Loop Label1.Text += frames.ToString & vbCrLf frames = 0 firstTime = timeGetTime Do Until timeGetTime - firstTime > 999 frames = frames + 1 Loop Label1.Text += frames.ToString End Sub [/code] My results were: VB6: 7046999 - GetTickCount 5660505 - timeGetTime VB.Net 8798485 - GetTickCount 6452133 - timeGetTime It is my current opinion that this accounts for the speed difference---but I know I my tests were not kosher. Maybe I'll do the job properly later with QueryPerformance counter. BTW, I did try removing the DoEvents stuff, but it didn't account for the gap. Sorry if I'm boring you guys---I find this stuff interesting!
Eric ColemanI'm surprised that code even works at all for you. A LONG data type in VB6 isn't the same thing as a LONG data type in VB.NET. When declaring windows API calls in VB.NET you have to make sure the data type size is the same as what's returned and what's used as parameters, otherwise you'll be reading from memory that you're not supposed to, and VB.NET will convert stuff that it shouldn't be converting.
game_makergreat ^^ I think you missed some small thing in your code becouse you calculate (timeGetTime - firstTime > 999 ) and frames = frames + 1 and the loop circle all of these takes time (ns),,, so it's not exactly what we looking for I mean : [code] Private Declare Function timeGetTime Lib "winmm.dll" () As Long Private Declare Function GetTickCount Lib "kernel32" () As Long Private Sub Command1_Click() Dim lastTime As Single Dim firstTime As Long Dim x1 As Long, x2 As Long Dim frames As Long Dim Count1 As Long '--------Speed of one count firstTime = GetTickCount Count1 = GetTickCount - firstTime '--------Speed of the loop firstTime = GetTickCount lastTime = firstTime Do Until lastTime - firstTime > 999 lastTime = lastTime + 1 Loop lastTime = GetTickCount - firstTime - Count1 Form1.Print lastTime '--------- frames = 0 firstTime = GetTickCount Do Until GetTickCount - firstTime > 999 frames = frames + 1 Loop x1 = frames - lastTime Form1.Print x1 '--------- frames = 0 firstTime = timeGetTime Do Until timeGetTime - firstTime > 999 frames = frames + 1 Loop x2 = frames - lastTime Form1.Print x2 Form1.Print (x1 - x2) / x2 * 100 & " %" End Sub [/code] This gives you exactly 100% FPS ( I think [8)] ) By the way this is not frame rate this is CPS (Calls / Second) witch we wan't to find (the speed of calling a function ) I got (VB.6) : 0 * 3440201 3205927 7.307% this means to me GettickCount is faster to call * : (my added code is not important here with fast functions and fast computers ) It's important to report the bug you find to microsoft [:)]
Lachlan87
quote:
A LONG data type in VB6 isn't the same thing as a LONG data type in VB.NET
That I understood. I had thought that VB.Net performed the conversions automatically, but I guess I must have been thinking of the other way of using the windows API (dllImport or something like that). game_maker: I realize my "test" was backwards and not particularly accurate, but even still, I thought it was safe to infer that GetTickCount was faster. Oh, well. At least my problem was fixed, and I managed to learn some things. . . but like Eric said, I still wonder why timeGetTime and other API functions work if VB.Net isn't auto converting things.
Sr. Guapo
quote:
I still wonder why timeGetTime and other API functions work if VB.Net isn't auto converting things.
Maybe they do that now in VB .NET to discourage the use of variants... Dunno... What if you defined the GetTickCount as an "Int32" in .NET, I think that is all that a long is, a 32 bit integer. Correct me if I'm wrong.
game_makerin VB.6 : Long is 4 byte so it's : 2^(8 * 4) = 4294967296 we dived it by 2 (to get plus and minus numbers) 2^(8 * 4) / 2 = 2147483648 to 2147483648 we save one bit to determine (minus or plus sign) -2147483648 to 2147483647 we need to find the same properties in VB.Net : in VB.Net Integer here defined as 4 byte and accept minus so it's the same definition in VB.6 Longs ... as you did !!! why microsoft did this ,,, if the have to change something then they should define something that have (integers in 8 bytes) and not changing everything[:)] and for Long it's = 8 byte as Double in vb.6 and the definition is not stable ,,it depens on wither it's 32-bit or 64-bit
Dan
quote:
Integer here defined as 4 byte and accept minus so it's the same definition in VB.6 Longs ... as you did !!! why microsoft did this ,,, if the have to change something then they should define something that have (integers in 8 bytes) and not changing everything
I think it has somthing to do with the fact the .NET framework is made up of the supposed 'Best bits' of VB and C. I believe C#'s Int/long variable types are 4 / 8 bytes respectivly. so it came to a toss up between who keeps the type definitions and C# won... makes sense really - Its easier to put a small parcel in a a big box, a wee bit trickier to do the reverse :)
VBBR
quote:
and for Long it's = 8 byte as Double in vb.6
That's wrong, because a Double is a floating-point value but a Long is an integer number.
quote:
and the definition is not stable ,,it depens on wither it's 32-bit or 64-bit
Right, but for C++ only. VB6 and .NET currently only work with the Win32 platform, that is, 32 bits. (4 bytes) (I dunno how would one write a 64-bit application in VB. I guess it's impossible.)
game_makerDan: Yes they have to make every definition consistence with every language specially on .Net FrameWork ,,, and they have to expand the ability of Integers types ... and VB.Net made from C# (as I heard) ... etc But still if you asked me what is better : 1) defining LongInteger (8-bit) or what ever they name it 2) changeing the definition for Integer Type and Long Type So, Clearly I will prefare 1 over 2 [:)] VBBR : You are very right ,,, in VB.Net they are difference in representation but they are now both (8 bytes) and this what I ment [:I]