Timed events handler (frame rate control for games etc.)-VBForums
Results 1 to 10 of 10

Thread: Timed events handler (frame rate control for games etc.)

  1. #1

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Timed events handler (frame rate control for games etc.)

    This is a useful class module for games and some applications which need regular and precise update (music players, media players...). It basically allows you to set events happen certain times in a second. The timing is much more accurate than what you can get with a Timer control, it really does the best it can to ensure being on time.

    Features:
    • Add any amount of timed events.
    • You can set several events with same ID and handle things by ID.
    • Efficiently prevents 100% processor usage. No freezing.
    • Lightweight: the only thing that could be really made faster would be to convert it to a normal module. I chose class module because it is easier to use and understand.
    • Skip processing if late: this is a must, because all computers might not be fast enough. You can see what happens when you try do 10000 updates a second for something that isn't lightweight.


    I've included a sample project which shows a very basic use for the code.


    How one would use this in a game?
    • Make one very often used event for checking controls, moving players, AI and other objects, detect collisions... (ie. trigger this code 240 times/second)
    • Screen update, drawing what is visible on the screen (for example 60 times/second)
    • Show information on frame rate (FPS) once in a second.
    • For code clarity, you might want to separate things to their own procedures instead of writing a long Main sub.



    Important!
    There are some rules that you should follow. You should avoid loops within loops, strings (especially if concatenations or coercions are involved), variants... anything that is even remotely slow. Even one slow thing can cause code to run very slowly if the code is repeated many times within a second.

    Remember to compile your games with all advanced optimizations turned on! You'll see how much faster they can get
    Attached Files Attached Files
    Last edited by Merri; Apr 30th, 2007 at 05:38 PM.

  2. #2

    Thread Starter
    VB6, XHTML & CSS hobbyist Merri's Avatar
    Join Date
    Oct 2002
    Location
    Finland
    Posts
    6,654

    Re: Timed events handler (frame rate control for games etc.)

    The following example shows what it means to have this class vs. what it would be like to be without it.

    In the example we target for 1000 loops per second. We set clsTick to do this and it does it rather accurately, only going off by a few ms of a full second at maximum.

    Then we do Sleep 1: DoEvents 1000 times. Seeing we call Sleep so that the program should rest for 1 ms, one could assume that by looping it 1000 times we run for one second or maybe a bit more seeing that we are using DoEvents as well.

    Note that difference for the latter is drastically different under IDE vs. compiled. You should always test/benchmark compiled because that is the final environment.
    Attached Files Attached Files

  3. #3
    Hyperactive Member
    Join Date
    Dec 2014
    Posts
    290

    Re: Timed events handler (frame rate control for games etc.)

    I get "runtime-error" 16 : expression too complex when I run from IDE, works compiled

    On Error Resume Next: Debug.Assert 0.1: On Error GoTo 0

    running the "test", it gives me:
    clsTick: 0,9987 - 0,9984 - 0,9987 - 0,9994 - 0,9991
    Sleep+DoEvents: 0.9994 - 1,0018 - 1,0003 - 0,9997 - 0,9999

    running in windows 7 64bit

  4. #4
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    3,923

    Re: Timed events handler (frame rate control for games etc.)

    The fact that you're running in windows 7 64bit has little bearing on the result you will get. The timing you get when calling Sleep is dependent on what the base clock period that sleep is based on is set to on your particular machine, regardless of what operating system you're running. And even on a given machine the time can differ from one time to another because the base clock period is a global parameter that any software can change (timeBeginPeriod x) at will so will affect all programs that use that clock.

    I've had older machines that when asked to sleep for 1ms would sleep for 1ms.
    I've had newer machines that when asked to sleep for 1ms would sleep for 15.625 ms.
    One of my old XP machines would sleep for 10ms when asked for anything 10 or less.
    The machine I'm on at the moment is sleeping for 2ms when asked for 1ms.

    On a given machine (Win7 laptop), after a fresh boot, I've had the sleep be 15.625 ms when asked to sleep for 1ms, but later in the day would sleep for 1ms when asked to. Obviously some software wanted the more precise timing so changed the base clock to provide events at the highest rate.
    The fact that the Sleep API uses a clock that fires at a rate that is subject to the whims of any software program makes using it disheartening. The fact that it could change while your software is running so that doing an initial test of the interval is no guarantee that you can compensate for the rate at the beginning and not have to worry about it while your program is running is a big part of why I find it disheartening.

    I haven't looked at the code to see how Merri is doing his timing, but I did run it and had no problem running it in the IDE or as a compile executable. Makes me wonder why you would have an issue in the IDE. As I mentioned above, when I run the example as is, it reports around 1ms with Merri's approach and around 2ms with the Sleep 1, which indicates the shortest period for the Sleep API call on this machine (at the moment) is 2ms.

    p.s. I did take a quick look at Merri's code and it basically compensates for the possible large sleep intervals by catching up by doing several events quickly with no sleep.
    The result is while you do execute 1000 events over the period of a second, depending on the length of your Sleep API minimum period, those events come in bursts. For instance, if you wanted 1000 events per second so called Sleep to wait for 1ms and 15ms later is wakes your process up, the code would do 15 events back to back to catch up. Then it might try to sleep for 1ms again and wake up 15ms later and do another burst of 15 events to catch up. Because he has DEFAULT_LATETICKS setup as 20 (which I believe is 20 ms), then even the fact that your are essentially almost 15ms late (for the first of the 15 needed to catch up), it isn't flagged as late, so all 15 late ticks (ranging from 15ms late to 1ms late or on time) are processed in that burst.

    Whether processing the events in repeated bursts is acceptable or not depends on what your software is doing. A stand alone model doesn't matter, but a model that is interacting with hardware or another program with strict timing requirements between events would have problems.
    Last edited by passel; Oct 17th, 2017 at 02:36 PM.

  5. #5
    Fanatic Member
    Join Date
    Apr 2015
    Location
    Finland
    Posts
    550

    Re: Timed events handler (frame rate control for games etc.)

    One note... Timing is not deterministic - as there is no realtime kernel included in Windows Desktop OS's.

  6. #6
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,530

    Re: Timed events handler (frame rate control for games etc.)

    The reason for that specific error is an IDE thing when using: Not someArray()
    This line is the reason: blnNoArrayInit = (Not Tick) = -1. The workaround is to call Debug.Assert App.hInstance or some other non-zero value which Merri was doing. Kind of surprised it failed with that error.

    Here's a thread where Merri talks about that error and avoiding it when testing for uninitialized arrays.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Icon Organizer/Extractor}
    {VB and DPI Tutorial} {XP/Vista Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  7. #7
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,498

    Re: Timed events handler (frame rate control for games etc.)

    If anyone's looking for a stable way to test if an array is initialized, you can use a typelib alias to GetMem4.

    the MIDL looks like this, and it works with all arrays (UDT and String) without unicode conversion, assuming you're using midl and not mktyplib.
    Code:
    	    [entry("GetMem4"),helpstring("Returns a pointer to an Array's SAFEARRAY structure. Use RefAry() to get a pointer to an Array variable.")]
    									HRESULT AryPtr(	[in] SAFEARRAY(void)* Ptr,	[out,retval]	PTR*	Address);
    	    [entry("GetMem4"),propget,helpstring("Get/Set an Array's SAFEARRAY reference pointer. R/W version of AryPtr()")]
    									HRESULT AryRef(	[in] SAFEARRAY(void)* Ref,	[out,retval]	PTR*	Address);
    	    [entry("PutMem4"),propput] 	HRESULT AryRef(	[in] SAFEARRAY(void)* Ref,	[in] 			PTR		Address);
    It's stable, unlike debug.assert workaround, which will not prevent your app from throwing an intermittent error 16 (even when compiled).
    Code:
    On Error Resume Next: Debug.Assert 0.1: On Error GoTo 0

    This is a lot more readable.

    Code:
    Public Function Add(ByVal FramesPerSecond As Double, Optional ByVal ID As Long = 0, Optional ByVal NoFrameSkip As Boolean = False) As Long
        Dim lngNewIndex As Long
        
        ' check for invalid input values
        If ID < 0 Then Add = -1: Exit Function
        If FramesPerSecond <= 0 Then Add = -1: Exit Function
        
        ' if array is initialized, we can get UBound
        If AryPtr(Tick) Then lngNewIndex = UBound(Tick) + 1
        
        ' add new item
        ReDim Preserve Tick(lngNewIndex)
        ' set settings
        With Tick(lngNewIndex)
            .ID = ID
            .Freq = CCur(CDbl(m_curFreq) / FramesPerSecond)
        End With
        ' return the new index
        Add = lngNewIndex
    End Function

    Given that we've seen intermittent error 16 from compiled apps over the years, all related to the The Not Array() hack, i don't think it should be used.
    Not to mention that it destabilizes the IDE. (i think Olaf has testified to this, where TheTrick i believe still uses Not Array)
    Last edited by DEXWERX; Oct 19th, 2017 at 02:46 PM.
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE

  8. #8
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,530

    Re: Timed events handler (frame rate control for games etc.)

    Not sure why a counter is not used for initializing & sizing arrays. When array is initialized set the counter to the number of elements. If the counter is zero, array not initialized -- not TLBs, no Not Not and Assert workarounds. Is it worth the extra trouble vs. just using a byte to 4 byte variable that can be of multi-use?
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Icon Organizer/Extractor}
    {VB and DPI Tutorial} {XP/Vista Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

  9. #9
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,498

    Re: Timed events handler (frame rate control for games etc.)

    Quote Originally Posted by LaVolpe View Post
    Is it worth the extra trouble vs. just using a byte to 4 byte variable that can be of multi-use?
    Whatever is easiest / stable in the long run is the goal, so utilizing another variable totally works. Incidentally Merri was already using one in the original routine anyway... so why bother with the hack (worst of both worlds)?

    For me however an AryPtr function is an elegant solution, that's as simple as checking off a reference. (that brings with it a bunch of other elegant solutions to pointer / memory issues)

    An Edge case that may not matter, VB will initialize an object array (but empty) if you pass it to a function, where other types of arrays will stay un-initialized (ie: throw an error if you attempt to use UBound).
    Imagine what it would be like to set breakpoints in, or step through subclassing code;
    and then being able to hit stop/end/debug or continue, without crashing the IDE.

    VB6.tlb | Bulletproof Subclassing in the IDE

  10. #10
    VB-aholic & Lovin' It LaVolpe's Avatar
    Join Date
    Oct 2007
    Location
    Beside Waldo
    Posts
    16,530

    Re: Timed events handler (frame rate control for games etc.)

    To any interested. By using a separate variable, it is very useful when the array may be buffered, i.e., larger than needed. The counter variable can be used to indicate how many array elements are actually used. The difference between the UBound and the counter is the buffer. Of course, if the counter value is zero, then this can be confusing: is it zero because array wasn't initialized or is it zero because the array is sized (all buffered), but no elements are in use. In that scenario, best to erase the array when counter reaches zero so there is no confusion.
    Insomnia is just a byproduct of, "It can't be done"

    Classics Enthusiast? Here's my 1969 Mustang Mach I Fastback. Her sister '67 Coupe has been adopted

    Newbie? Novice? Bored? Spend a few minutes browsing the FAQ section of the forum.
    Read the HitchHiker's Guide to Getting Help on the Forums.
    Here is the list of TAGs you can use to format your posts
    Here are VB6 Help Files online


    {Alpha Image Control} {Memory Leak FAQ} {Unicode Open/Save Dialog} {Icon Organizer/Extractor}
    {VB and DPI Tutorial} {XP/Vista Manifest Creator} {UserControl Button Template} {stdPicture Render Usage}

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Featured


Click Here to Expand Forum to Full Width

Survey posted by VBForums.