Smooth frame rate/Vsync

Started by bobjob, March 03, 2009, 04:54:22

Previous topic - Next topic

bobjob

Under Windows XP Iv found that when running a Display at 60hz (frequency) with Vsync on looks smooth.
but if the monitors refreshrate is increased to 70 (using a CRT monitor). the application runs jaggy when I also use Display.sync(60). It seems that vSync is fine, so there isnt visible rectangles, but it jumps a bit.

If Display.sync isnt called the framerate jumps to 70 (with vSync on).

I figured that id just force fullscreen to be 60hz with vsync on and sync(60)
for window mode: turn off vSync and call sync(60).

I was wondering if there were anyway to make it seem smooth if the monitor refresh rate is higher than 60.

I noticed in both doom3 and quake4 the frame rate is capped to 60, and runs fine if vSync is on during window mode (framrate still capped at 60 and no visible rectangles).



Any ideas how the guys at ID did this?

A few things i considered were:
* That it is actually drawing at 70frames per second, but logic is only being calculated at 60 (doubtful).
* It somehow gets the desktop frequency and doesnt update every few frames.
but I tried changing the frequency during runtime in quake4 and still the framerate was capped at 60, and ran smooth. still possible if it gets the desktop frequency every game cycle (again doubtful).
* Is it possible that somehow the game window in quake4 is independant of the desktop refreshrate?


Ciardhubh

Quote from: bobjob on March 03, 2009, 04:54:22
Under Windows XP Iv found that when running a Display at 60hz (frequency) with Vsync on looks smooth.
but if the monitors refreshrate is increased to 70 (using a CRT monitor). the application runs jaggy when I also use Display.sync(60). It seems that vSync is fine, so there isnt visible rectangles, but it jumps a bit.

If Display.sync isnt called the framerate jumps to 70 (with vSync on).

If Vsync is on and the refresh rate is 70, vsync tries to synchronise to 70 frames per second. If you additionally call Display.sync(60), you only send 60 frames to the graphics card. That's obviously not enough. Your actual frame rate drops to 35 (because the front buffer gets drawn multiple times while waiting for the back buffer to finish drawing), which can look laggy.

There's a pretty good explanation here:
http://www.hardforum.com/showthread.php?t=928593

Quote from: bobjob on March 03, 2009, 04:54:22
I figured that id just force fullscreen to be 60hz with vsync on and sync(60)
for window mode: turn off vSync and call sync(60).

I was wondering if there were anyway to make it seem smooth if the monitor refresh rate is higher than 60.

Vysnc AND sync at 60 might also drop your FPS to 30, since vsync is (AFAIK) goverened by the driver and sync by LWJGL. These two aren't necessarily synchonised.

I assume your game logic is hard-coded to 60 fps (which is probably a design issue, since slow computer might not be able to run 60)? Otherwise you could just drop the sync(60) call and only use vsync. If your game logic is capped at 60 (or less), you could run it in a different thread than rendering. Render at vsync rate and compute game logic at 60.

Quote from: bobjob on March 03, 2009, 04:54:22
I noticed in both doom3 and quake4 the frame rate is capped to 60, and runs fine if vSync is on during window mode (framrate still capped at 60 and no visible rectangles).

Any ideas how the guys at ID did this?

A few things i considered were:
* That it is actually drawing at 70frames per second, but logic is only being calculated at 60 (doubtful).
* It somehow gets the desktop frequency and doesnt update every few frames.
but I tried changing the frequency during runtime in quake4 and still the framerate was capped at 60, and ran smooth. still possible if it gets the desktop frequency every game cycle (again doubtful).
* Is it possible that somehow the game window in quake4 is independant of the desktop refreshrate?

As far as I know vsync is controlled by the graphics driver. So when you manually change the refresh rate in the driver (or go to window mode which probably uses the desktop refresh rate), the game should automagically adjust to that new rate. How do you know FPS really stayed at 60? Maybe they alternatively changed the desktop refresh rate while the game was running?

Actually point one sounds reasonable. Separating logic and rendering is pretty much standard in big engines, isn't it? Plus I doubt their million dollar engines are hard-coded to a certain rate.

bobjob

Quote from: Ciardhubh on March 03, 2009, 09:45:04
If Vsync is on and the refresh rate is 70, vsync tries to synchronise to 70 frames per second. If you additionally call Display.sync(60), you only send 60 frames to the graphics card. That's obviously not enough. Your actual frame rate drops to 35 (because the front buffer gets drawn multiple times while waiting for the back buffer to finish drawing), which can look laggy.
yeah i figured this, thats why in window mode, I only use sync(60)

Quote
Vysnc AND sync at 60 might also drop your FPS to 30, since vsync is (AFAIK) goverened by the driver and sync by LWJGL. These two aren't necessarily synchonised.
nah that doesnt happen because the smart guys ;) who work on LWJGL made sync(framerate) work out to be framerate +1 or something like that. Also not all driver configurations will let you use vSync therefore you need a backup capping method therefore sync is needed.

Quote
I assume your game logic is hard-coded to 60 fps (which is probably a design issue, since slow computer might not be able to run 60)? Otherwise you could just drop the sync(60) call and only use vsync. If your game logic is capped at 60 (or less), you could run it in a different thread than rendering. Render at vsync rate and compute game logic at 60.
The target computers should run fine, but if they dont its a simple fix to skip frames. Thats not really the problem. I would rather not use another thread for such a simple game, if in the future I needed to load resources dynamically I might consider it, but its a bit excessive otherwise.

Quote
As far as I know vsync is controlled by the graphics driver. So when you manually change the refresh rate in the driver (or go to window mode which probably uses the desktop refresh rate), the game should automagically adjust to that new rate. How do you know FPS really stayed at 60? Maybe they alternatively changed the desktop refresh rate while the game was running?
Correct. In window mode (windows XP) vSync uses the desktop refreshrate. in quake4 and doom3 you can show frames per second "com_showFPS 1" so its simple to observe that the frames havnt increased, as im sure such a high quality game would get an accurate reading from the clock.
the screen doesnt change at all when you launch id's games in window mode (except gamma). So I highly doubt that the game changes the desktop refresh rate.

Quote
Actually point one sounds reasonable. Separating logic and rendering is pretty much standard in big engines, isn't it? Plus I doubt their million dollar engines are hard-coded to a certain rate.
If there is no java friendly solution, I think the best way to approach the problem is with my current way of capping window mode with sync(60). For fullscreen attempting vSync as well as capping with sync forcing a display mode frequency of 60.

broumbroum

Quote from: bobjob on March 03, 2009, 04:54:22
(...)but if the monitors refreshrate is increased to 70 (using a CRT monitor). the application runs jaggy when I also use Display.sync(60). It seems that vSync is fine, so there isnt visible rectangles, but it jumps a bit.

(...)A few things i considered were:
* That it is actually drawing at 70frames per second, but logic is only being calculated at 60 (doubtful).
* It somehow gets the desktop frequency and doesnt update every few frames.
but I tried changing the frequency during runtime in quake4 and still the framerate was capped at 60, and ran smooth. still possible if it gets the desktop frequency every game cycle (again doubtful).
* Is it possible that somehow the game window in quake4 is independant of the desktop refreshrate?
I'd say that refresh rates do not influence in any way the ASPECT a game is working. Indeed, when projecting a game, the first thing to state is HOW the rendering is being implemented. Usually the case is that rendering is done EVERY FRAME that passes on screen, that means NO REFRESH RATE can state that skipping frame is needed or not (for you say "It somehow gets the desktop frequency and doesnt update every few frame").
Desktop freq. has nothing to do in terms of game cycle. That is, a GAME CYCLE IS INDEPENDENT of any system clock frq, so far the graphics runs standard calls to graphics rendering. If that wouldn't be the case, Quake BOTS would be the smarter the faster the system clock runs, WHICH HAS NO SENSE.
As a matter, rendering must behave the same at ANY RATES and hence, the TIMING IS IMPORTANT to further understand how to implement the rendering.
I think you are computing a rendering that realizes frames by the frame-rate of the system clock and not by a common TIMING rate.
Look into GOOGLE FOR ANIMATION FRAMEWORK and maybe that would help you a bit, if possible ! :)

bobjob

I attempted to fix frame rate smoothing with vSync on in desktop mode, with a refresh rate greater than 60.

Sadly without results. Heres my attempt. My videocard is considerably fast, but i can still visually see the skipped frames.

tested with 70 and 72 Hertz
void run() {
    int frameSkip, frame = 0;
    boolean skipFrames = true;
    {
        //Code required to get the current refreshrate
        java.awt.GraphicsEnvironment ge = java.awt.GraphicsEnvironment.
                getLocalGraphicsEnvironment();
        java.awt.GraphicsDevice g = ge.getDefaultScreenDevice();
        java.awt.DisplayMode dm  = g.getDisplayMode();
        int refreshRate = dm.getRefreshRate();
        frameSkip = refreshRate - FRAME_RATE;
        if (frameSkip <= 0) skipFrames = false; 
        else frameSkip = refreshRate / frameSkip;
    }
    while (running) {
        frame++;
        if (frame > frameSkip) frame = 0;
        if (Display.isCloseRequested()) running = false;
			
        if (Display.isActive() && Display.isVisible()){
            if (skipFrames && frame == 0) {
                Display.update();
                continue;
            } 
            logic();
            render();
            //Display.sync(FRAME_RATE);
            Display.update();
        } else {
            Display.sync(FRAME_RATE);
            Display.update();
        }
    }
}

anyone got any ideas of how to fix this, or another approach to take?

broumbroum

So you want to skip frames if the refresh is greater than what you select as FRAME_RATE, don't you ?
As I mentioned previously on this topic, TIMING calculation is necessary. I can see you only compare the target System-refresh to the FRAME_RATE you select. What's TIMING or how can explain "my" concept ?
Shortly presented : TIMING calculation occurs when you are doing something that repeats frequently in a process. TIMING is computed very easily with Java :
System.currentTimeMillis() or System.nanoTime()
Now, to get on with FRAME_SKIPPING, which I may repeat I did not used to have it explicitely integrated, it's as easy as marking the CURRENT FAME_RATE TIMING :
/** your run()*/
void run() {
    // (...)
    int skip = 0;
    while(running) {      
      if(isTargetFPS() || skip >= 5) {
         skip = 0;
         logic();
         render();
         Display.update();
         markFPS();
     } else {
         skip++;
     }
}
/***/
long myLastTime = 0;
/***/
void markFPS() {
     myLastTime = System.currentTimeMillis();
} 
/***/
boolean isTargetFPS() {
     long current = System.currentTimeMillis();
     /**Frame-Per-Second*/
     float FPS = (float)(current - myLastTime) / 1000f;
     int skip = (int)Math.round(FPS - FRAME_RATE);
     /** define as you want a targeted range, e.g. 5 frames */
     if(skip > 5) {
         return false;
     } else return true;
}

This may give you some idea of measuring TIMING.
::)

bobjob

my concern is not timing. I understand how to get a desired framerate. The only assumption my code makes, is that the frame will be rendered within the framerate (which is the case with my current video card).

What im having trouble with is when vSync is on. I would like to take advantage of the fact that vSync makes frames visually whole. But the problem is that vSync is relative to the monitor refresh rate.

I dont want to have to render the screen 70 fps, when the gameloop are only 60 cycles ps.

this is perfect when the monitor refresh rate is 60.

As for your timing methods, its recommended to use the function Sys.getTime();
which is called with the Display.sync(int) function. So there is no problem with timing.

bobjob

Anyway for now I think Ill just use this method. Because I really like the idea of vSync in window mode. And I dont want to start another thread for game logic, or re-render the same scene multiple times.
  private static float getFrameSkip() {
    int refreshRate = java.awt.GraphicsEnvironment
        .getLocalGraphicsEnvironment()
        .getDefaultScreenDevice()
        .getDisplayMode()
        .getRefreshRate();
    if (refreshRate == java.awt.DisplayMode.REFRESH_RATE_UNKNOWN) return 0;
        float frameSkip = refreshRate - FRAME_RATE;
        if (frameSkip <= 0) return 0;
    else return refreshRate / frameSkip;
  }

  public static void run() throws Exception {
    boolean skipFrames = false;
    int frame = 0;
    float leftOver = 0;
    float frameSkip = getFrameSkip();
    if (frameSkip > 0) skipFrames = true;

    while (running) {

      if (Display.isCloseRequested()) running = false;

      if (Display.isActive() && Display.isVisible()){
        frame++;
        if (!skipFrames && frame > FRAME_RATE) { //if desktop is running at 60hz
          frame = 0;
          frameSkip = getFrameSkip(); 
          if (frameSkip > 0) skipFrames = true; //check to see if display is still running at 60hz

        } else if (skipFrames && (frame/frameSkip) + leftOver >= 1) {
          //SKIP FRAME ON EVEN INTERVAL

          leftOver = (frame % frameSkip) + leftOver;
          frame = 0;
          Display.update();
          frameSkip = getFrameSkip();//get screen frequency in case of change
          if (frameSkip <= 0) skipFrames = false;
          continue;
        }
        logic();
        render();
        Display.sync(FRAME_RATE); //IN CASE OF NO VSYNC
        Display.update();
      } else {
        Display.sync(FRAME_RATE);
        Display.update(); //REQUIRE UPDATE TO DETERMINE IF VISIBLE
      }
    }  
  }

broumbroum

Well, OK vSync is a solution, but I'm not sure that you can find another way to shrink the FPS. Why are your gameloop constrained to 60Hz ? Can you give a sample of that gameloop 60Hz ?
By the way, my short samples can't be correct, because I've not tested it, especially thinking about frame skip. As I've said it in my first post, I don't see any reason to shrink a game loop freq. because I'll make them independant of the graphics rendering but rather to set the logic timing PROPORTIONNALLY to the refresh rate.
For example, I've got a player whose Fighter is an Animation Model running forwards, that's pos.x += speedPerSecond * (CURRENTTIME - TIME) / 1000f. Let's suppose I'd have set up the speed as a pos.x += (SPEEDPERFRAME = 30px), this would crash whenever the refresh rate vary, which is thouroughly an evidence and not correct.  :-[

bobjob

Quote from: broumbroum on March 12, 2009, 18:32:38
Well, OK vSync is a solution.
I dont think you really understood my original problem, vSync is what i was having trouble with.

pos.x += speedPerSecond * (CURRENTTIME - TIME) / 1000f

code like this is (IMO) is not really efficient.
Your program will be running flat stick. I can only see problems when porting to a different computer, I think different computers have different timer resolutions "1000f".  The sort of float equation makes it harder to calculate the accuracy of positions if they should be treated as int's. It also means any moving variable needs to have this long calculation, making it relative to the time passed, rather than just a basic increment (eg pos.x += 1).

If you want an effective way of setting a good framerate use the function Display.sync(int frameRate);