Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Posted
2005-12-03 22:40:43 » |
|
http://www.angelfire.com/clone/wolfbane/loop2.jar (cut and paste please) The jar file contains a loop (one is by KevGlass the other is by David Brackeen). I am trying to achive smooth animtion at around 60 fps. Currently it is not smooth, it stutters a the time. My question is, is the performance i am getting correct? Using the loop I use, am I seeing the results I am suppose to see (with stutter). The mainClass.java contains the code for the loops. you can activate any version by decomenting it in the constructor. Can someone pease take a look and let me know what you think? I have seen games run very smoothly at 60 fps, how can I get my demo to run smoothly as well?
|
Kommi
|
|
|
Vorax
Senior Devvie    Projects: 1
System shutting down in 5..4..3...
|
 |
«
Reply #1 - Posted
2005-12-03 22:44:20 » |
|
I can't open the jar - Winzip and 7zip say it's invalid.
|
|
|
|
kevglass
|
 |
«
Reply #2 - Posted
2005-12-03 23:08:11 » |
|
Jar works fine here.
The loop you've got from the spaceinvaders code isn't very accurate on reporting FPS so if thats what you're using to monitor the "smoothness" then it's not going to work out. Second, with a target frame time of 15ms you're actually aiming for 66.666666r fps which probably isn't too great.
Kev
|
|
|
|
Games published by our own members! Check 'em out!
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #3 - Posted
2005-12-04 00:06:44 » |
|
hey Kev, you kind of answered my question. So you are saying that 66 fps is not good enough? How is it then that I see games runing smoothly at 60 fps and less?
Second point of confusion. My monitor refresh is set to 85 hz. If I set it to 60hz then the loop runs lot smoother. Can someone please confirm that. Also what is the reason for this, considering that I am limiting the fps anyway?
|
Kommi
|
|
|
kevglass
|
 |
«
Reply #4 - Posted
2005-12-04 00:32:17 » |
|
Its not the 66 fps target that I was pointing out really. The question is really how are you determining the "smoothness" ?
Kev
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #5 - Posted
2005-12-04 01:15:23 » |
|
um... visually. I can see visually that the box moving around jerks too often. Do you see that as well? Or is it consistent in it's movement for you?
|
Kommi
|
|
|
Jeff
|
 |
«
Reply #6 - Posted
2005-12-04 01:27:28 » |
|
hey Kev, you kind of answered my question. So you are saying that 66 fps is not good enough? How is it then that I see games runing smoothly at 60 fps and less?
Second point of confusion. My monitor refresh is set to 85 hz. If I set it to 60hz then the loop runs lot smoother. Can someone please confirm that. Also what is the reason for this, considering that I am limiting the fps anyway?
ALomost certainly your frame rate limit isnt working if that chnages things.... Anglefire is refusing your link.
|
|
|
|
tom
|
 |
«
Reply #7 - Posted
2005-12-04 01:42:08 » |
|
It is likely you've created a test case that the eye is particularly good at detecting unsmooth movement. As an example, a single colored quad over a single color background will only good if you sync it to refresh and move a multiple of 1 pixel at a time.
|
|
|
|
Jeff
|
 |
«
Reply #8 - Posted
2005-12-04 01:47:24 » |
|
A few things just ocurred to me:
(1) What are you calling a "stutter"? Are you sure it isn't a tear? Tearing happens when you update the image in the mdist of a scan. Scan synching removes tearing.
(2)WHenever you see momentary irregular pauses in a Java game its a good idea to run your profiler and look at your memory situation. Object leaks can cause this by causing needless full GC runs.
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #9 - Posted
2005-12-04 02:09:28 » |
|
Jeff you have to cut and paste the link. By "stutter" I mean that the box in my demo doesnt move very smoothly. I am sure it is not tear. It is also a very basic demo and I dont think GC is the issue here, although I could be wrong of course. frame rate not working.. hum. Here is the code I dont see what I got wrong since it is a copy of Kev's code Loop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| long lastLoopTime = getTime(); long lastFpsTime = 0; int fps = 0; while (true) { long delta = getTime() - lastLoopTime; lastLoopTime = getTime(); lastFpsTime += delta; fps++; if (lastFpsTime >= 1000) { totalFps = fps; lastFpsTime = 0; fps = 0; } update_KevGlass(delta); render(); sleep(lastLoopTime+15-getTime()); } } |
update: 1 2 3 4 5 6 7 8 9 10 11 12 13
| if (x + 50 >= width) { currDx = -dx; x = width - 50; } if (x < 0 ) { currDx = dx; x = 0; } x += currDx * delta; |
The getTime() and sleep() are the GAGE timer methods. I have looked asll over and still cannot figure out why at 85hz I get choppy motion, while at 60Hz I get smooth motion. I do not chagne the code and call the sleep() method at the end of each cycle.
|
Kommi
|
|
|
Games published by our own members! Check 'em out!
|
|
Jeff
|
 |
«
Reply #10 - Posted
2005-12-04 03:52:24 » |
|
Ill try to run it this weekend and have a look.
Right now my suspicion is that what youa re seeing is tearing.
Tearing would be particualrly obvious moving a simple flat geometric shape like a reasonably large sized single-color box...
|
|
|
|
Vorax
Senior Devvie    Projects: 1
System shutting down in 5..4..3...
|
 |
«
Reply #11 - Posted
2005-12-04 05:02:41 » |
|
If it looks good at 60, but not 85 - the question is what refresh rate is your monitor at? I would bet (not a lot of money, but some  ) it's at 60 Mhz.
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #12 - Posted
2005-12-04 05:35:39 » |
|
It looks good at 85, and my refresh is ta 85. The prob I currently see it is that if I let the loop run as fast as possible (no sleep at all) then it is smooth at 85 and smooth at 60 hz refresh. But if I set a pause to like 10, then I get stutter when I am getting 67fps. I am now beggingin to suspect that neither Thread.yeild() nor Thread.sleep() are of good enough granuality to sleep an accurate amount of time. But I am using GAGE timer which has a sleep method that I am hoping is accurate.
So now a new question arises, which method does everyone usefor fixed rate fps? Thread.Yield(), Thread.Sleep(), or the sleep method in GAGE Timer? Is there another way?
|
Kommi
|
|
|
Jeff
|
 |
«
Reply #13 - Posted
2005-12-04 06:48:36 » |
|
Yes.
Use 1.4 or later and the nanosecond timer.
|
|
|
|
oNyx
|
 |
«
Reply #14 - Posted
2005-12-04 06:53:31 » |
|
*Totally* jumpy here. My (rather old) mainboard has some shite chipsets which causes QPC (which is used by nanoTime) to leap. Didnt happen with win9x... does happen with 2k and above. Well, duh. I used to use some adaptive yield loop+nanoTime, but that doesnt work anymore... for me anyways. There arent many machines affected... so you could just ignore the issue. Well, unfortunately I cant ignore it  It caps the maximum time to spend per frame at some point and if its too late with this frame, it will hurry a bit more with the next one (so with triple buffering you wont drop a frame if some frames took a tad too long). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| private long timeNow, timeThen, timeLate = 0; [...] g.dispose();
long gapTo = 1000000000L/75L + timeThen; do{ Thread.yield(); timeNow = System.nanoTime(); }while(gapTo > timeNow+timeLate);
if(gapTo<timeNow) timeLate = timeNow-gapTo; else timeLate = 0;
timeThen = timeNow;
strategy.show(); |
The other method, which I'm using now uses currentTimeMillis millis again, sorta averages the last 10 frames and adjusts the amount of yielding accordingly. Seems to work fine if the time per frame doesnt fluctuate too much. (I havent tested it that much yet, but it seems to work fine.) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| private long lastFrame=0; private long []fa=new long[10]; private int faptr=0; private float yield=1f; [...] long timeNow = System.currentTimeMillis(); fa[faptr++]=timeNow-lastFrame; if(faptr>9)faptr=0; lastFrame=timeNow; long sum=0; for(int i=0;i<10;i++) sum+=fa[i]; if(sum>160) yield*=0.95f; else if(yield<1f) yield=1f; else yield*=1.05f; for(int i=0;i<yield;i++) Thread.yield(); |
Feel free to experiment with different factors and/or less/more frames for averaging.
|
|
|
|
Jeff
|
 |
«
Reply #15 - Posted
2005-12-04 07:06:32 » |
|
*Totally* jumpy here. My (rather old) mainboard has some shite chipsets which causes QPC (which is used by nanoTime) to leap. Didnt happen with win9x... does happen with 2k and above. Well, duh.
PLEASE bug report this. If there is hardware out there that nanotimer is failing on I think the JDK team needs to know.
|
|
|
|
oNyx
|
 |
«
Reply #16 - Posted
2005-12-04 11:38:06 » |
|
*Totally* jumpy here. My (rather old) mainboard has some shite chipsets which causes QPC (which is used by nanoTime) to leap. Didnt happen with win9x... does happen with 2k and above. Well, duh.
PLEASE bug report this. If there is hardware out there that nanotimer is failing on I think the JDK team needs to know. I wondered about that. "Returns the current value of the most precise available system timer, in nanoseconds." I mean even with leaping it still does what it says on the tin. Nothing more... nothing less. Its some hardware issue, which can happen with some of those ancient (5+ years old) chipsets. Eventually worth a note in the javadoc comment, but thats about it. Details on the issue can be found here: http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&--- Two years ago I filed some bug report. It got ignored... and the bug is still there (transparent frame icons doesnt work with JFrame.setDefaultLookAndFeelDecorated(true)). Easy to understand and easy to reproduce... so, I dont see much of a reason to go through that hassle again. Especially with something like this, which is extremely hard to reproduce (because you need such faulty hardware) and it will dissappear before it gets addressed anyways.
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #17 - Posted
2005-12-04 15:59:18 » |
|
Thanks i will try your approaches. Jeff I am using 1.5 and the GAGE Timer which uses nanoTime, or nanoTime itself in the other loop examples in the code.
|
Kommi
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #18 - Posted
2005-12-04 18:04:54 » |
|
Ok I have made the modifications using Onyx's Yield code. I get the same results as I do with Gage timer sleep code. Both examples can be found here: (cut and paste the links please)
Kevin's space invaders loop using GAGE 1.5 nanoTimer and the GAGE sleep method angelfire.com/clone/wolfbane/loop_KevGlass.jar
David Brackeen's loop using the NanoTimer() and Onyx's yield code. angelfire.com/clone/wolfbane/loop_NanoTimer.jar
I have included the file mainClass.java where all the different loops reside. I am still seeing stutter, can you guys test it and let me know what you think my problem is. I beleive that I should be able to get smooth animation at 60fps going. I come to this conclusion because if I let the loops run as fast as possible (ie no sleep ) then I get smooth animation at both 85hz refresh and 60hz refresh.
To see an example of this , go to any one of the loops, remove the sleep or yield code (depending which loop you are looking at), set your monitor refresh to 60hz, then run the code. You will see that while the fps stays the same, the stutter is not there when removing the yield code.
|
Kommi
|
|
|
Jeff
|
 |
«
Reply #19 - Posted
2005-12-04 20:14:40 » |
|
*Totally* jumpy here. My (rather old) mainboard has some shite chipsets which causes QPC (which is used by nanoTime) to leap. Didnt happen with win9x... does happen with 2k and above. Well, duh.
PLEASE bug report this. If there is hardware out there that nanotimer is failing on I think the JDK team needs to know. I wondered about that. "Returns the current value of the most precise available system timer, in nanoseconds." I mean even with leaping it still does what it says on the tin. Nothing more... nothing less. Its some hardware issue, which can happen with some of those ancient (5+ years old) chipsets. Eventually worth a note in the javadoc comment, but thats about it. Details on the issue can be found here: http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323&Fair enough, but I stil lthin kthey should include a note and the link in the docs. It will ultimately save them grief, so Id BR it just as "Nanotime inconsistant on old x86 motherboards, should be a note in the docs" or something like that  Thanks JK --- Two years ago I filed some bug report. It got ignored... and the bug is still there (transparent frame icons doesnt work with JFrame.setDefaultLookAndFeelDecorated(true)). Easy to understand and easy to reproduce... so, I dont see much of a reason to go through that hassle again. Especially with something like this, which is extremely hard to reproduce (because you need such faulty hardware) and it will dissappear before it gets addressed anyways.
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #20 - Posted
2005-12-05 23:08:40 » |
|
Updated question.
I have asituation where my time_pre_frame in milliseconds is the following
17 17 17 17 63 <-- huge jump due to loop taking too long 10 17
What is the correct course of action in the situation where my time_pre_frame varies greatly for one frame or two frames? Ideally I need each frame to take the same amount of time, if they dont then how what can be done to compensate?
I find this to be one of my problems i am thinking it is the gc kicking in.
|
Kommi
|
|
|
Vorax
Senior Devvie    Projects: 1
System shutting down in 5..4..3...
|
 |
«
Reply #21 - Posted
2005-12-05 23:22:52 » |
|
What is the correct course of action in the situation where my time_pre_frame varies greatly for one frame or two frames? Ideally I need each frame to take the same amount of time, if they dont then how what can be done to compensate?
You can't compensate for the visual loss - the frame time is gone. You can compensate for game play though with a lag timer for your moving entities. In general: A lag timmer is simply a multiplier that is adjusted based on how fast the frame was rendered. If your target speed is 15ms then the muliplier is 1. If a frame took 30ms your multiplier would be 2. All movement in the game is multiplied by the multiplier. So say if a character was supposed to move 10 pixels, but the last frame took 30ms to render, once the multiplier is applied, the character would move 20 pixels. This complicates things like collision detection and animation though, it's tricky to take it into account for those things, but it's more a matter of experimentation depending on how your code goes. A good test is to introduce lag and see if the game still plays the same as if the FPS was high. That should be the net effect.
|
|
|
|
Jeff
|
 |
«
Reply #22 - Posted
2005-12-06 00:39:16 » |
|
So as Vorax says, you need to compensate the motion because you will never get the same time for each frame.
There are a couple of things people do:
(1) Introduce a max frame tate. By limiting your rate to the low end of your normal spread you hide minor variations. This is done just be introducing a sleep() at the end of the frame calculation to take up the left over time for frames that get done faster..
(2) "Skip frames" when there is an unusually long delay. You could do this simply by multiplying your movement by the extra time BUT if you are doing per-frame collision detection you will risk missing collisions that way. A preferrable way is to loop over the frame calculations and actually do multiple frames "move and react" calculations but no render until you are caught up, then render the final result.
(3) You really shouldnt be seeing significant gc pauses on JDK1.5+ or later on a properly architected game. If I were you I'd profile that puppy and take a good look at both where youa re using a lot of CPU and whats happening to your heap.
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #23 - Posted
2005-12-06 01:26:35 » |
|
I dont mean to sound desperate but can some one please help me with this? The demo is here: (cut and paste please) http://www.angelfire.com/clone/wolfbane/gameLoop_KevGlass.jarThe file is mainClass.java, and the method is called public void gameLoop_KevGlass(). If you run the jar file it will runthe loop for 10 seconds and then exit. A txt file is created with the delta's written in. For me after the 4th second I ususally see the jumps start to occur. I am new to profiling code and will try my best but any help will be greatly appreciated. I have never been able to resolve this problem.
|
Kommi
|
|
|
Jeff
|
 |
«
Reply #24 - Posted
2005-12-06 01:33:14 » |
|
I dont mean to sound desperate but can some one please help me with this? The demo is here: (cut and paste please) http://www.angelfire.com/clone/wolfbane/gameLoop_KevGlass.jarThe file is mainClass.java, and the method is called public void gameLoop_KevGlass(). If you run the jar file it will runthe loop for 10 seconds and then exit. A txt file is created with the delta's written in. For me after the 4th second I ususally see the jumps start to occur. I am new to profiling code and will try my best but any help will be greatly appreciated. I have never been able to resolve this problem. If you are using either Eclipse or Netbeans, get their pro0filer plug ins. Theya re free and I know that the Netbwans one at least is pretty easy to use. Frankly, if yo uare writing data to a file its possible that just the flush of the cache of output is causing your pause, you really need a less intrusive measuring tool and thats what profilers are all about. I hoenslty dont know if Ill have time to hit your code with the Profiler msyelf, but I'll see...
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #25 - Posted
2005-12-06 01:51:10 » |
|
Thanks Jeff, and no I know that it is not the write operation, since without it I get the same effect.
Hum. now that I look at it closer I can see that it only jumps in the begging and the rest of the time it remains pretty constant. So I guess the cause was something loading.
But still one thing really bothers me, why is it that when I artificially set a frame cap at say 65 fps, the movement still appears jerky, while if I let it run as fast as possible but change the screen refresh to 60hz, the movement is a lot smoother. Can anyone run a similar test and tell me if they see the same results?
Just go to gameLoop_KevGlass() method, set the sec variable to something like 20, and then either leave the sleep call at the bottom of the while loop for a frame cap, or comment it out for a fast as possible run. Any feedback will be much appreciated, thanks.
I just ran the test myself and noticedthat there is no difference in the time deltas that are passed to the update method. The movement is updated by x += dx * delta. So the deltas are the same for both a frame capped version, anda non capped 60 hz refresh version. Is thee something I am missing here? maybe haing to do with rendering and the refresh? I see no reason why it is not smooth in both vesions.
|
Kommi
|
|
|
Jeff
|
 |
«
Reply #26 - Posted
2005-12-06 05:20:09 » |
|
Thanks Jeff, and no I know that it is not the write operation, since without it I get the same effect.
Hum. now that I look at it closer I can see that it only jumps in the begging and the rest of the time it remains pretty constant. So I guess the cause was something loading.
Ahhhh. ANother possability if you are running on server VM is that you are hitting a pause for compilation. A lot of games will "warm up" the VM or force it to compile up front (I *think* the flag for that is -Xcompile but I might be wrong. One of the guys here should know.) You might want to try that and see if it changes anything. Is thee something I am missing here? maybe haing to do with rendering and the refresh? I see no reason why it is not smooth in both vesions.
Id need to run it and play with ti a bit. If i get the time Il llet you know.
|
|
|
|
erikd
|
 |
«
Reply #27 - Posted
2005-12-06 12:13:23 » |
|
I think the reason that it's smooth when you set the monitor refresh rate to 60fps is that you throttle to ~67fps. It is probably being nicely vsync'ed so you really get exactly 60fps. When you set your refresh rate to 85, your game loop can't keep up, because you're aiming at ~67fps, so what happens is that one 'game frame' takes more than one 'monitor frame', so it will miss vsync at least 1 out of 2 frames, leaving you with only 50% of 85fps and maybe even an irregular framerate. Try to set your target framerate in your throttling to something slightly higher than the monitor refresh rate, but don't remove your throttling code (in case vsync is disabled).
|
|
|
|
Kommi
Junior Devvie  
All opinions will be lined up and shot!
|
 |
«
Reply #28 - Posted
2005-12-06 15:13:10 » |
|
Thanks Erikd, that sounds like the most logical explanation, I will give it a try when I get home. Is this a common problem/solution? If my demo was written in C++ would the issues still exist? Is the issue of having the frame time take longer than the refresh time a Java issue?
|
Kommi
|
|
|
jbanes
|
 |
«
Reply #29 - Posted
2005-12-06 22:18:57 » |
|
Thanks Erikd, that sounds like the most logical explanation, I will give it a try when I get home. Is this a common problem/solution? If my demo was written in C++ would the issues still exist? Is the issue of having the frame time take longer than the refresh time a Java issue? This is true of all languages. A rule of thumb is that animation is always smoothest when it's synced with the hardware. However, you can get away with reasonably smooth animation as long as your framerate is far in excess of the hardware. (e.g. 130 FPS looks just fine on a 60Hz monitor) BTW, look at the bottom of the GAGE Homepage for an algorithm that automatically adjusts to variances in the time to generate a frame. It should help smooth out your animation when VSync is disabled.
|
|
|
|
|