Announcement

Collapse
No announcement yet.

Could you show a small Delphi example for the new TTimer (2.01) ?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Could you show a small Delphi example for the new TTimer (2.01) ?

    Unfortunately I am unable to get the new concept of the Timer from version 2.01.
    Since there are no more onTimer and onProcess events how do you handle these now? Do I have to check for these Tokens? And is there something else for the maxframerate property to limit the framerate for the rendering?

    Of course I looked up the examples but those don't seem to use the timer, some just create it but nothing more. No similar thing to onProcess.

    Edit: So, as I could find out the Tokens value gets increased according to the speed setting so by checking if it's not zero at the end of the main rendering event I can call the process loop? Is this the way now to do it? And if the rendering loop is slower than the speed setting will "Tokens" be 2 or more then and I need to call the processloop that often to ensure processing happens at the specified speed? Still don't know how to limit the rendering (old onTimer) framerate when vsync is off though.
    Last edited by Zimond; 12-17-2021, 05:58 PM.

  • #2
    OK. So maybe I have figured it out myself. Just need confirmation that this is the way to do it now.

    I wanted a limit for the main rendering loop while having a fixed rate for the processloop. I simply created 2 timer, one called Processtimer. I set the first to something like 250 and used the nextslice function on the renderloop which effectivly limits the framerate to 250. The processtimer is set to 60 and I check for the tokens of the processtimer and call the processloop accodingly. And that seems to work. I put a counter in the processloop that resets after 60 to see if ticks like in seconds. And I also tested heavily increasing the renderingwork (with a loop) to force the render framerate down under 60 to see if the processloop is still called 60 times per second.

    Comment


    • #3
      I will be creating a separate post explaining well how new timer works. First, make sure to set timer's speed, which by default is 60 (frames per second). To reproduce previous behavior with OnProcess event, you can do something like this:
      Code:
        if FSwapChain.BeginScene then
        try
        finally
          FSwapChain.EndScene;
        end;
        FTimer.Update;
      
        FTokens := FTimer.Tokens;
      
        for I := 0 to FTokens - 1 do
          OnProcess(Self);
      If TTimer.Update is called more often than its speed, then periodically it will be adding tokens and calling OnProcess above will result in exactly 60 times per second, even if rendering is done faster. If TTimer.Update is called less often than its speed (i.e. slow rendering), then it'll be likely adding more than one token each time, so calling OnProcess will still occur 60 times per second no matter what. This replicates exactly the behavior of previous timer module. Similarly, if you are rendering only when needed, you should put TTimer.Update to OnIdle event. The code with tokens should go there as well.

      However, if you want to limit the rendering speed, or do fixed-rate rendering, you should add FTimer.NextSlice at the beginning of above code, e.g.:
      Code:
        // Alternatively, next line can be changed to FTImer.UpdateNextSlice and you can get rid of TTimer.Update call below.
        FTimer.NextSlice;
      
        if FSwapChain.BeginScene then
        try
        finally
          FSwapChain.EndScene;
        end;
        FTimer.Update;
      
        FTokens := FTimer.Tokens;
      
        for I := 0 to FTokens - 1 do
          OnProcess(Self);
      In above code, TTimer.NextSlice will make sure that the code after it will execute in the next consecutive time slice immediately following previous call to TTimer.NextSlice (or TTimer.Restart, or when the timer was first created). So, for example, if you need to render a video at 29.97 FPS, you set speed to 29.97, call (Update)NextSlice function and use TTimer.TimeSlice as frame reference, e.g.:
      Code:
        FTimer.UpdateNextSlice;
      
        if FSwapChain.BeginScene then
        try
          RenderVideoFrame(FTimer.TimeSlice); // The frame number corresponds to timer's slice.
      
          Caption := 'Frame #' + IntToStr(FTimer.TimeSlice) + ', skipped frames: ' + IntToStr(FTimer.SkippedTimeSlices);
        finally
          FSwapChain.EndScene;
        end;

      Comment


      • #4
        As for the second question, I think you understood the functionality correctly. You can use one timer to limit rendering speed to some value, e.g. 250 FPS, while using another timer to retrieve tokens at a different speed, e.g. 60 FPS. You can also do both at the same time, but rendering speed will be limited to the processing speed.

        Also, if you are rendering only when needed, then you'll most likely end up using two timers - one for fixed-rate processing in OnIdle event using tokens, another to measure rendering frame rate.

        Comment


        • #5
          Thanks for the explanation. As long as two Timers dont have any further disadvantages, I will use them like this. I also thought about using some kind of multiplicator. For example set the speed to 240fps but then only process every 4th token to get back to 60fps processing speed.

          Off topic:
          I thought about timing in games a lot. Of course the easiest way for the developer is to use a fixed rate like for example 120 and thats just the limit. (Which is very tempting, and probably the reason why so many PC games still have the fps locks of the console versions) I guess the only way to really have no FPS limit and being able to support even super fast 240hz monitors is to make the processing dynamic and account for the time it takes from one frame to another when you move anything in the game. And that sounds like a hell of a hassle when it comes to determinism of the gameplay. I remember that in Quake 3 you could actually jump a little bit higher when your fps went up. At 120fps you could reach some items that you couldn't reach with 70-80fps.
          I guess a mix might be the best. Just put certain non critical elements into the renderpath like camera movements or particle animations, while gameplay critical stuff in handled in the fixed rate path.


          Comment


          • #6
            Originally posted by Zimond View Post
            Thanks for the explanation. As long as two Timers dont have any further disadvantages, I will use them like this. I also thought about using some kind of multiplicator. For example set the speed to 240fps but then only process every 4th token to get back to 60fps processing speed.
            Timer is a rather lightweight module so using more than one should have no side effects. Regarding using every 4th token - I think it would be best to use an accumulator for that. In other words, add tokens to the accumulator and when its value is bigger or equal to 4, subtract 4 and process one frame.

            Originally posted by Zimond View Post
            I thought about timing in games a lot. Of course the easiest way for the developer is to use a fixed rate like for example 120 and thats just the limit. (Which is very tempting, and probably the reason why so many PC games still have the fps locks of the console versions) I guess the only way to really have no FPS limit and being able to support even super fast 240hz monitors is to make the processing dynamic and account for the time it takes from one frame to another when you move anything in the game. And that sounds like a hell of a hassle when it comes to determinism of the gameplay. I remember that in Quake 3 you could actually jump a little bit higher when your fps went up. At 120fps you could reach some items that you couldn't reach with 70-80fps.
            I guess a mix might be the best. Just put certain non critical elements into the renderpath like camera movements or particle animations, while gameplay critical stuff in handled in the fixed rate path. [/I]
            Fixed-rate processing simplifies design a lot and in majority of cases is enough to provide smooth movement, beyond which the difference will be less noticeable by a human eye. There are monitors with refresh rate higher than 60 (in fact, I have two - one with 144 Hz refresh and another with 170 Hz one), but they are less common; most of them also support variable refresh rate, which would adapt better to your rendering frame rate. So for most games, I would suggest sticking to fixed rate processing.

            For the other approach that you mention, the dynamic processing, TTimer provides Latency, which gives you time in microseconds between this and previous frame. To perform object movement, you would do something like this:
            Code:
              LTimeOffset := FTimer.Latency * 60.0 / 1000000.0; // Should match fixed-rate processing at 60 FPS
              FPosition := FPosition + FVelocity * LTimeOffset;
            This approach is being used in most CAD projects that I've been involved with and it is also used by an upcoming UI system in the framework. However, in games, especially in multiplayer games, this can make design much more complicated, especially for things like interpolation and validation. But as you have said, you can use mixed approach - for instance, move objects at 60 FPS, but the camera and some animations can be processed dynamically as fast as monitor's refresh rate.
            Last edited by lifepower; 12-20-2021, 05:54 PM. Reason: Adjusted divisor for latency in code snippet.

            Comment

            Working...
            X