[FIXED] Accurate Display.sync()

Started by kappa, February 20, 2012, 22:52:04

Previous topic - Next topic

kappa

Any difference with the latest nightly build of LWJGL? also can you profile your lwjgl app to see where most of the time is being spent and if indeed in the sync() and Thread.yield() methods. The sync() method should really be sleeping for most of its time (especially on mac), so really shouldn't be using 30%-70% of the CPU.

Obsyd

I'm going to test/profile it on sunday!

Obsyd

56% in Display.sync()
15% in swapBuffers
4% in getTime
...

Tested it with the latest build.
I noticed that this high % problem happens when the app window is out of focus and I do something like exposé! (or anything in the background)
And the % will go up to 90 and slowly normalize to 1-2%. (really slowly)

kappa

hmm, that is really odd, can't reproduce that on Window or Linux. Could be a OS X specific issue, will see if I can reproduce the problem on an OS X machine.

Can't really see how Thread.sleep(1) mostly in the method could cause so much CPU usage when the window becomes unfocused (the code doesn't have anything to do with window focus).

Does the problem happen with the previous Display.sync() that was used by LWJGL? There was an odd looking "synchronized (GlobalLock.lock)" in that method which might have been a fix for this issue.

spasi

Obsyd: Could you please post your render loop code? It might help us reproduce the issue.

kappa, the new sync code works great here. The LWJGL gears sample with sync(60) runs at a solid 299 frames / 5 seconds (59.8 fps), with 2% CPU usage.

Obsyd

Non thread.yield versions(2.8.3) work fine. (1-2%)
A really simple loop. All it does is randomly draw 96 quads.

while(Display.isCloseRequested() == false && Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) == false){

benchmark.benchmarkrender();

Display.update();
Display.sync(60);
}

public static void benchmarkrender() {
        glClear(GL_COLOR_BUFFER_BIT);
        for (int i = 0; i < 96; i++) {

            int x = veletlen.veletlenfv(560) + 4;
            int y = veletlen.veletlenfv(400) + 4;

            init_textures.benchmark.bind();
            glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2f(x, y);
            glTexCoord2f(1, 0);
            glVertex2f(x + init_textures.benchmark.getTextureWidth(), y);
            glTexCoord2f(1, 1);
            glVertex2f(x + init_textures.benchmark.getTextureWidth(), y + init_textures.benchmark.getTextureHeight());
            glTexCoord2f(0, 1);
            glVertex2f(x, y + init_textures.benchmark.getTextureHeight());
            glEnd();
        }
    }

kappa

I've had a chance to test this on OS X now and can confirm your issue. It seems that any form of Thread.yield() or even an empty while loop (for even a ms or so a frame) burns excessive amounts of CPU on OS X (much more than on Windows or Linux). However the OS X Thread.sleep() timer seems much more accurate that both Windows and Linux.

I've adjusted the Display.sync() method now to fix this issue and CPU usage should be back to normal on OS X. Please do test and confirm with the next nightly build of LWJGL.

Obsyd

The new Display.sync seems perfect :)

Obsyd

Well, it is perfect on OSX, but I booted up a win7 on my iMac and the same problem happened as before.
As before, if the window is out of focus and you are doing things in the background (browsing the net,or grab the window and toss it around a little), the CPU percentage goes up and when the window regains focus it will SLOWLY start to drop to 0-2%.
Goes up to 30-40%.
Again, simple loop and advanced loop.

I think thread.yield is not the best way.

kappa

hmm, something sounds really odd, not really sure how having the window focused or not should change the amount of CPU being used by Display.sync(), wasn't able to reproduce that on OS X yesterday or on Windows and Linux.

Quote from: Obsyd on March 19, 2012, 04:25:27
I think thread.yield is not the best way.
Do keep in mind that Thread.yield() should only be running for a small fraction of the Display.sync() time with the majority of time being spent in Thread.sleep().

Also it seems to work fine for you while the Display is focused, so using 30-40% of the system resources while not focused sounds way too excessive, something is seriously wrong somewhere.

One theory that I can think of which might cause the above is that when the Display is not focused then somehow Thread.sleep(1) becomes massively inaccurate and the Display.sync() method increases the usage of Thread.yield() to try to compensate, which would explain the above behaviour. I suppose a potential fix could be to only use Thread.yield() while the Display.isActive() (and thus lower the accuracy when not focused).

However I haven't been able to reproduce the above behaviour after testing on multiple computers and OS's. Can anyone else confirm the above problem?

What type of CPU are you using?
What Java version are you running on windows?

Obsyd


kappa

Ah right, thanks for that. I know what the issue is now and can reproduce it. Basically what happens is when you minimise and maximise the windows randomly, like in the video, or even hold and drag the window (basically any windows event that freezes the lwjgl app), the Thread.sleep(1) massively oversleeps and the auto tuning code increases the rate of Thread.yield() use in an attempt to adapt to the varying timer (until it maximises to a point where its running for the full Display.sync()).

If you do leave the window stable for a while, you'll notice the CPU usage slowly decrease back to normal (can take like 30+ seconds with the current setting). However it decreases too slowly but it does decrease, I've got a fix for it now, however need to test on a few different systems before committing.

Simon Felix

Compiled our game with yesterdays nightly build. Today the players said the following:
4x "super smooth" (XP, Win7)
1x "smooth for a few secs, then not smooth again" (Win7)

The one who said "smooth for a few secs, then not smooth again" said it's currently unplayable. The game tries to sync to 120fps, previous version ran at 125fps, the current version runs at 110-120fps on his computer. To me it appears that the new code is over-adjusting, then backing off, then over-adjusting again, etc.. Could that happen?

Maybe you could adjust the code so that it always uses yield for the last few ms, for extra safety. And btw: Limiting the yield-time to 15ms (or so) might be useful too, just to avoid extreme cases.
Download Cultris II, the fastest Tetris clone from http://gewaltig.net/

kappa

@Obsyd do test next nightly build, added a fix to avoid the excessive CPU using on windows now, so should adapt much faster now and be a lot more robust.

Currently experimenting with Riven on a new better adapting algorithm, will see what comes from that, early results from new algorithm are really promising.

@dr_evil yeh yield time is capped to handle extreme cases, but do test the latest changes.

kappa

Quote from: dr_evil on March 21, 2012, 19:33:10
The one who said "smooth for a few secs, then not smooth again" said it's currently unplayable. The game tries to sync to 120fps, previous version ran at 125fps, the current version runs at 110-120fps on his computer. To me it appears that the new code is over-adjusting, then backing off, then over-adjusting again, etc.. Could that happen?
Yeh its theoretically possible, if that is indeed the case then it needs to be tweaked a little more but i'll run a few tests with higher fps like 120fps, so far mostly just been testing with 60fps.