Author Topic: Bug and improvement report  (Read 4269 times)

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 592
  • Legacy of Kain: Revival is completed!!! (2 years)
    • View Profile
    • Email
Re: Bug and improvement report
« Reply #30 on: 2011-01-28, 12:45:53 PM »
Beats me!  But it does.
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2710
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Bug and improvement report
« Reply #31 on: 2011-01-28, 02:42:23 PM »
Wait, so how does it stay at 60 fps if you only call sleep(0)?

Because it does it over and over until the stopwatch says that it's time to go to the next frame.  The hope is that instead of simply looping and checking, adding a call to Sleep(0) inside the loop would give a little more time back to the CPU.

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2710
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Bug and improvement report
« Reply #32 on: 2011-01-29, 09:33:12 AM »
Vincent, can you test this fix for me and tell me if it limits the frame rate better?  I tried to make it use sleep to sleep for the correct time to average out correctly over 5 frames instead of just since the last frame.  Here are the changes in GeneralRules.cs:

Line 18 had a declaration for previousFrame.  Change it to this:

Code: [Select]
   protected static long[] previousFrames = new long[] { 0, 0, 0, 0, 0 };
   protected static int previousFrameIdx = 0;

Change the LimitFrameRate function to this:
Code: [Select]
   public virtual void LimitFrameRate(int fps)
   {
      long freq;
      long frame;
      freq = System.Diagnostics.Stopwatch.Frequency;
      frame = System.Diagnostics.Stopwatch.GetTimestamp();
      long oldFrameTime = previousFrames[previousFrameIdx];
      while ((frame - oldFrameTime) * fps < freq * previousFrames.Length)
      {
         int sleepTime = (int)((oldFrameTime * fps + freq * previousFrames.Length - frame * fps) * 1000 / (freq * fps));
         if (sleepTime > 0) System.Threading.Thread.Sleep(sleepTime);
         frame = System.Diagnostics.Stopwatch.GetTimestamp();
      }
      previousFrames[previousFrameIdx] = frame;
      previousFrameIdx = (previousFrameIdx + 1) % previousFrames.Length;
   }

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 592
  • Legacy of Kain: Revival is completed!!! (2 years)
    • View Profile
    • Email
Re: Bug and improvement report
« Reply #33 on: 2011-01-29, 10:54:32 AM »
Okay, I'll try this at work on monday. :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 592
  • Legacy of Kain: Revival is completed!!! (2 years)
    • View Profile
    • Email
Re: Bug and improvement report
« Reply #34 on: 2011-01-31, 11:20:44 AM »
Okay, with your test code, in my FpsTester program I get a frame rate that shifts between 56 and 58 fps (still requesting 60 fps).

I tried it in my game also, to see if get the same result, and I've got the same results but when I play the game, it looks like the rate isn't very fluid.  It's kinda stroboscopic a little.  It's a little jumpy for the eyes.  With sleep(0) it runs better.

Does that help?

Thanks! :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2710
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Bug and improvement report
« Reply #35 on: 2011-01-31, 02:59:05 PM »
After an experiment, I think sleep(0) is not really better than leaving sleep out completely.  It ends up taking all the CPU it can get on 1 thread.

My question now is this.  Is it better to have the original behavior where LimitFrameRate resulted in a frame rate lower than what you requested, or the new behavior where you get closer to the requested frame rate, but it's "kinda stroboscopic"?  Which behavior do you think SGDK2 should deliver by default?  Or should I have multiple LimitFrameRate functions:
1. LimitFrameRate (same behavior as now)
2. LimitFrameRateExact (same implementation without Sleep)
3. LimitFrameRateAvg (The new implementation from above that's "kinda stroboscopic")?

How does it behave if you change "protected static long[] previousFrames = new long[] { 0, 0, 0, 0, 0 };" to "protected static long[] previousFrames = new long[20]"?

Edit: Someone else from stackoverflow.com has been discussing the problem with me and has predicted this behavior.  Hopefully that person can also propose a better solution.  You can see the discussion at http://stackoverflow.com/questions/4837416/high-precision-sleep-or-how-to-yield-cpu-and-maintain-precise-frame-rate
« Last Edit: 2011-01-31, 03:20:17 PM by bluemonkmn »

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 592
  • Legacy of Kain: Revival is completed!!! (2 years)
    • View Profile
    • Email
Re: Bug and improvement report
« Reply #36 on: 2011-02-01, 09:48:42 AM »
I tried with the new array.  The FPS is around 60 fps this time, it is more precise.  But it is still very jumpy.  I move my character around and it's very bad.  He walks fast, stops, walks fast, stops again, continuously like this.  Very annoying, it ruins the whole game I think.

When it comes to fps, I don't like to compromise.  When I play a game, I make sure the graphics are not high enough to slow the game.  So, in this case, of course I would say drop the sleep method altogether.  The stroboscopic version is the definitely the worst.  I prefer the game to be a little slower but constant and fluid.

I think the best version if the sleepless one.  Yeah, it does drain all the CPU, but unfocus the game window and you're okay.  You won't get problems by changing computers too.

With the actual version, if someone develop a game on a computer where the lower fps problem occurs, he might boost the fps parameter in the LimitFrameRate call and then the game will become much too fast for other computers.  It's okay if the game is meant to created and played on the same computer, but otherwise it's risky.


The "stroboscopic version" that I would call the "unstable version", is just painful.  I would avoid it at all costs.

I don't know if providing different versions of the behavior is a good idea.  It should be explained what each version of the method implies.  I would definitely go with the sleepless one.

But then again, you have to take my word for it, since it would seem I'm the only one with this issue...   :P
Legacy of Kain: Revival completed!
http://lokrevival.webs.com

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2710
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Bug and improvement report
« Reply #37 on: 2011-02-01, 02:22:34 PM »
I have another idea that will probably be better than the one above.  I think the stroboscopic effect happened because the first 5 frames were done instantly, then the next frame waits for 5 frames (or N frames) worth of time, and the following 4 frames run instantly again.  I was worried about this problem and tried to think about it without anything to write on, so I thought about it wrong, but now I see the problem.  Now I think it might work better to keep track of 2 things:
1. What was the timer on the first frame?
2. How many frames have passed since the first frame?

From that we can calculate whether we are ahead or behind the target FPS.  Now that I think about it, I think that's what the person from stackoverflow was suggesting.  But the word "decaying" threw me.  I think it makes sense now, though, to reset the "first frame" every once in a while in case something like a message box or switching to another window (pausing the game) causes one of these values to get out of sync.

I will try coming up with the code for that when I get home.

bluemonkmn

  • SGDK Author
  • Administrator
  • Fanatic
  • *****
  • Posts: 2710
    • ICQ Messenger - 2678251
    • MSN Messenger - BlueMonkMN@gmail.com
    • View Profile
    • http://sgdk2.sf.net/
    • Email
Re: Bug and improvement report
« Reply #38 on: 2011-02-01, 05:50:18 PM »
OK, try this.

Replace the first block with:
Code: [Select]
  protected static long fpsStartTime;
   protected static long fpsFrameCount;

and the second block with:
Code: [Select]
  public virtual void LimitFrameRate(int fps)
   {
      long freq;
      long frame;
      freq = System.Diagnostics.Stopwatch.Frequency;
      frame = System.Diagnostics.Stopwatch.GetTimestamp();
      while ((frame - fpsStartTime) * fps < freq * fpsFrameCount)
      {
         int sleepTime = (int)((fpsStartTime * fps + freq * fpsFrameCount - frame * fps) * 1000 / (freq * fps));
         if (sleepTime > 0) System.Threading.Thread.Sleep(sleepTime);
         frame = System.Diagnostics.Stopwatch.GetTimestamp();
      }
      if ((++fpsFrameCount > fps) || (fpsStartTime == 0))
      {
         fpsFrameCount = 1;
         fpsStartTime = frame;
      }
   }

Edit: I just tried this one on my laptop where even sleep(0) didn't work (if I recall), and this was really solid.  The frame rate was almost always exactly what I requested.  Hopefully it works as well for you.
« Last Edit: 2011-02-01, 05:57:52 PM by bluemonkmn »

Vincent

  • SGDK2 Addict
  • Expert
  • Fanatic
  • *****
  • Posts: 592
  • Legacy of Kain: Revival is completed!!! (2 years)
    • View Profile
    • Email
Re: Bug and improvement report
« Reply #39 on: 2011-02-02, 11:47:24 AM »
Wow, this one is very good!  Yup, you have a winner. :)
Legacy of Kain: Revival completed!
http://lokrevival.webs.com