Hello Xboing fans, This is a patch for version 2.4 of XBoing. A cool bloke called Alexander sent me this patch which helps to fix the game speed problem. His email is below followed by the patch. Just cut the patch code out and apply this to the source for xboing2.4 Good luck. PS: Don't forget to cut off my sig at the end of the patch! email message from Alexander :- I guess as a result of some confusion, SleepSync() invokes usleep() with the parameter of milliseconds, while BSD usleep() implemented in all the OS I am aware of requires microseconds. Moreover, the internally generated usleep() accepts milliseconds in one compilation instance and microseconds in another. Xboing 2.3 didn't sleep at all (except for the "SLOW_SPEED" game mode), so the frame rate was totally defined by the processor power while the variation of game speed was induced by the ball velocity changing in accordance with the "speedLevel". There is some improvement in Xboing 2.4. Now the SleepSync() accumulates the required sleeping time up until it exceeds 33.33 ms. For the default warp factor (5) it results in 6 frames being passed without delay followed by the invocation of usleep( 35 ) after the 7th. As no OS is capable to put a process asleep for a few microseconds due to the overheads involved in context switching, Linux (and I suspect the other OS too) uses a threshold resolution - the process is suspended until the next tick of the PSU timer, something like 20 or 16.66 ms. So somehow it works due to combination of bugs and the current technology limitations. However, no influence on the frame rate was achieved, the sleeping time does not depend on the parameter of usleep() (as long as it remains relatively small) but it depends on the phase of internal frame progress. This can result in a significant drop in the game speed if the processor is not fast enough meaning that 7 frames can not be processed within the timer resolution (16.66 ms). Correcting this bug won't help: If you replaced usleep( 35 ) by usleep( 35*1000 ) the game would be ridiculously slow. As it seems to me, the adequate solution is to synchronise the frame progress with the real time, i.e. to stabilise the frame rate at a particular level regardless of the processor power. The traces of such an attempt can be found in SleepSync(): the time taken by XSynch is taken out of the sleeping time. In the patch attached to this letter, SleepSync() is replaced by a simple FLL/PLL timer stabilising the frame rate according to its "ms" parameter (so it is 5ms/frame as a default). Because the both, the frame rate and the ball velocity are involved, I reduced the range of "userDelay" (see patch for "main.c"). Maybe it requires further reduction. Also the timings in "bonus.c" and "presents.c" were adjusted. Due to the real-time frame rates, they can now be calculated much more precisely. Regards, --- Alex Bolychevsky | voice: +44-1483-259000 x 2384 Dept of Electronic & Electrical Eng., | fax: +44-1483-34139 University of Surrey, | telex: 859331 UNIVSY G Guildford, GU2 5XH, United Kingdom | e-mail: a.bolychevsky@ee.surrey.ac.uk ------------------------ >8 ---- CUT HERE ---- 8< -------------------------- diff -u --recursive xboing/bonus.c xbpatch/bonus.c --- xboing/bonus.c Fri Nov 22 01:28:46 1996 +++ xbpatch/bonus.c Tue Jan 14 18:48:28 1997 @@ -311,7 +311,7 @@ XFlush(display); SetBonusWait(BONUS_SCORE, frame + 5); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); DEBUG("set bonus mode to BONUS_SCORE.") } @@ -326,7 +326,7 @@ { DEBUG("in function DoScore() in bonus.c") - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Nice message rewarding you for your efforts */ strcpy(string, "Congratulations on finishing this level."); @@ -336,7 +336,7 @@ ypos += (35 + GAP); SetBonusWait(BONUS_BONUS, frame + LINE_DELAY); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); DEBUG("set bonus mode to BONUS_BONUS.") } @@ -363,7 +363,7 @@ DrawShadowCentredText(display, window, textFont, string, ypos, blue, TOTAL_WIDTH); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Now skip to the next sequence */ SetBonusWait(BONUS_LEVEL, frame + LINE_DELAY); @@ -387,7 +387,7 @@ DrawShadowCentredText(display, window, textFont, string, ypos, blue, TOTAL_WIDTH); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Now skip to the next sequence */ SetBonusWait(BONUS_LEVEL, frame + LINE_DELAY); @@ -411,7 +411,7 @@ bonusScore += ComputeScore(SUPER_BONUS_SCORE); DisplayScore(display, scoreWindow, bonusScore); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Now skip to the next sequence */ SetBonusWait(BONUS_LEVEL, frame + LINE_DELAY); @@ -454,7 +454,7 @@ ypos += (int) (textFont->ascent + GAP * 1.5); firstTime = True; - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); } } @@ -468,7 +468,7 @@ { int secs, theLevel; - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Get the number of seconds left on the clock */ secs = GetLevelTimeBonus(); @@ -533,7 +533,7 @@ /* Play the sound for the super bonus */ if (noSound == False) playSoundFile("Doh3", 80); - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Get ready for the next sequence */ SetBonusWait(BONUS_TIME, frame + LINE_DELAY); @@ -569,7 +569,7 @@ SetBonusWait(BONUS_TIME, frame + LINE_DELAY); ypos += (textFont->ascent + GAP/2); firstTime = True; - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); } } @@ -583,7 +583,7 @@ { int secs = 0; - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Get the number of seconds left on the clock */ secs = GetLevelTimeBonus(); @@ -627,7 +627,7 @@ int myrank = 0; char str[5]; - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Obtain current ranking for this score */ myrank = GetHighScoreRanking(score); @@ -680,7 +680,7 @@ { DEBUG("DoEndText in bonus screen.") - SetGameSpeed(SLOW_SPEED); + SetGameSpeed(MEDIUM_SPEED); /* Finishing sentence - so you know what level to do */ sprintf(string, "Prepare for level %ld", level+1); diff -u --recursive xboing/main.c xbpatch/main.c --- xboing/main.c Fri Nov 22 01:28:46 1996 +++ xbpatch/main.c Tue Jan 14 18:32:30 1997 @@ -156,7 +156,7 @@ /* Set an entire game speedup or slowdown speed */ temp = (speed / (long) userDelay); - userDelay = delay; + userDelay = delay/2 + 3; speed = (long) (temp * userDelay); speedLevel = 10 - delay; } diff -u --recursive xboing/misc.c xbpatch/misc.c --- xboing/misc.c Fri Nov 22 01:28:46 1996 +++ xbpatch/misc.c Tue Jan 14 18:54:45 1997 @@ -89,8 +89,8 @@ #ifdef SYSV #ifdef __clipper__ struct timeval tv; - tv.tv_sec=((usec)/1000); - tv.tv_usec=(((usec)%1000)*1000); + tv.tv_sec=((usec)/1000000); + tv.tv_usec=((usec)%1000000); select(1,NULL,NULL,NULL,&tv); #else poll((struct poll *) 0, (size_t) 0, usec / 1000); /* ms resolution */ @@ -105,6 +105,68 @@ #endif #if NeedFunctionPrototypes +static int kudelay(unsigned long count) +#else +static int kudelay(count) +unsigned long count; +#endif +{ + unsigned int bluff = 1; + + while( count-- != 0 ) /* this can hardly be "optimised out" */ + bluff = (bluff ^ ((bluff & 1) * 0x6110)) >> 1; + + return( bluff ); +} + +#if NeedFunctionPrototypes +static void kusleep(unsigned long usec) +#else +static void kusleep(usec) + unsigned long usec; +#endif +{ + static float kuliber_time = 0.0; + static float kuliber_count = 0.0; + static unsigned long kutimerswap = (1000000 / 15); + struct timeval st, et; + long gap; + unsigned long count; + + if( kuliber_time == 0.0 ) + { + count = 0x1000; /* calibrate delay loop */ + gettimeofday(&st, NULL); + + while( True ) + { + kudelay( count ); + count = (count << 1); + gettimeofday(&et, NULL); + gap = (((et.tv_sec - st.tv_sec) * 1000000) + + (et.tv_usec - st.tv_usec) ); + + if( gap > 400*1000 ) + break; + } + + kuliber_time = (float)(gap/4); + kuliber_count = (float)(((count-1)&~0xFFF)>>2); + } + + if( usec > kutimerswap ) + { + kutimerswap = (1000000 / 30); + usleep( usec ); /* using usleep() for low resolution only */ + } + else + { + kutimerswap = (1000000 / 15); + kudelay( (unsigned long)(kuliber_count*((float)usec/kuliber_time)) ); + } +} + +#if NeedFunctionPrototypes void sleepSync(Display *display, unsigned long ms) #else void sleepSync(display, ms) @@ -112,27 +174,85 @@ unsigned long ms; #endif { - struct timeval st, et; - long SyncTime; - static unsigned long accu; + static long Expected_time = 0; + static long Ref_sec = 0; + static long Ref_usec = 0; + static long Sleeping_time = 0; + static int PLL_filter = 0; + struct timeval st; + long elapsed; + long dodelay; + float x; - gettimeofday(&st, NULL); XSync(display, False); - gettimeofday(&et, NULL); - SyncTime = (((et.tv_sec - st.tv_sec) * 1000) + - ((et.tv_usec - st.tv_usec) / 1000) ); + gettimeofday(&st, NULL); + + if( Ref_sec == 0 ) + { + Ref_sec = st.tv_sec; /* take the initial reference point */ + Ref_usec = st.tv_usec; + Sleeping_time = ms * 1000; + } + + elapsed = (st.tv_sec - Ref_sec) * 1000000 + (st.tv_usec - Ref_usec); + dodelay = Sleeping_time; + + if( PLL_filter > 0 && elapsed != 0 ) /* Phase lock loop */ + { + dodelay = Expected_time + Sleeping_time - elapsed; + + if( dodelay < 0 ) + { + dodelay = 0; PLL_filter--; + } + else + if( dodelay > 2*Sleeping_time ) + { + dodelay = 2*Sleeping_time; PLL_filter--; + } + else + PLL_filter = 3; + + if( elapsed > 40*Sleeping_time && Expected_time > 40*Sleeping_time ) + { + elapsed -= 20*Sleeping_time; /* Ref. point shouldn't be far away*/ + Expected_time -= 20*Sleeping_time; + Ref_usec += 20*Sleeping_time; + Ref_sec += Ref_usec/1000000; Ref_usec %= 1000000; + } + } -/* if ((ms) > ((1000 / 60) + SyncTime)) - usleep(ms - SyncTime); -*/ - if ((ms + accu) > ((1000 / 30) + SyncTime)) + if( PLL_filter <= 0 && elapsed > 40000 ) /* Frequency lock loop */ { - usleep(ms +accu - SyncTime); - accu = 0; + /* feedback filter */ + x = 0.5 + 0.5 * (float)Expected_time / (float)elapsed; + + if( x < 0.966 ) /* Acceleration must be strongly limited */ + x = 0.966; + + if( x > 2.0 ) + x = 2.0; + + Sleeping_time = (long)((float)Sleeping_time * x); + + if( Sleeping_time < 500 ) + Sleeping_time = 500; + + if( x > 0.99 && x < 1.01 && Sleeping_time > ms*500 ) + PLL_filter = -1 - 4*PLL_filter; /* locked - it's time to try PLL */ + else + PLL_filter = 0; + + Ref_sec = st.tv_sec; + Ref_usec = st.tv_usec; + Expected_time = 0; + dodelay = Sleeping_time; } - else if (ms > SyncTime) - accu += (ms - SyncTime); + + kusleep( dodelay ); + + Expected_time += ms * 1000; } #if NeedFunctionPrototypes diff -u --recursive xboing/presents.c xbpatch/presents.c --- xboing/presents.c Fri Nov 22 01:28:46 1996 +++ xbpatch/presents.c Tue Jan 14 18:35:40 1997 @@ -287,7 +287,7 @@ } #endif - SetPresentWait(PRESENT_TEXT1, frame + 800); + SetPresentWait(PRESENT_TEXT1, frame + 200); } #if NeedFunctionPrototypes @@ -309,7 +309,7 @@ RenderShape(display, window, justin, justinM, x, y, 285, 44, True); - SetPresentWait(PRESENT_TEXT2, frame + 300); + SetPresentWait(PRESENT_TEXT2, frame + 100); } #if NeedFunctionPrototypes @@ -331,7 +331,7 @@ RenderShape(display, window, kibell, kibellM, x, y, 260, 40, True); - SetPresentWait(PRESENT_TEXT3, frame + 500); + SetPresentWait(PRESENT_TEXT3, frame + 200); } #if NeedFunctionPrototypes @@ -355,7 +355,7 @@ RenderShape(display, window, presents, presentsM, x, y, 410, 44, True); - SetPresentWait(PRESENT_TEXT_CLEAR, frame + 750); + SetPresentWait(PRESENT_TEXT_CLEAR, frame + 250); } #if NeedFunctionPrototypes @@ -375,7 +375,7 @@ FadeAwayArea(display, window, x, y, 410, 44); - SetPresentWait(PRESENT_LETTERS, frame + 10); + SetPresentWait(PRESENT_LETTERS, frame + 5); } /* The distances for the gap inbetwen blocks */ @@ -404,7 +404,7 @@ DrawLetter(display, window, i, x, y); x += 10 + dists[i]; - SetPresentWait(PRESENT_LETTERS, frame + 300); + SetPresentWait(PRESENT_LETTERS, frame + 100); } else { @@ -416,7 +416,7 @@ x += dists[3]; DrawLetter(display, window, 3, x, y); - SetPresentWait(PRESENT_SHINE, frame + 200); + SetPresentWait(PRESENT_SHINE, frame + 100); } i++; @@ -463,7 +463,7 @@ if (in == 11) { XCopyArea(display, store, window, gc, 0, 0, 20, 20, x, y); - SetPresentWait(PRESENT_SPECIAL_TEXT1, frame + 500); + SetPresentWait(PRESENT_SPECIAL_TEXT1, frame + 100); } } } @@ -499,7 +499,7 @@ len = strlen(wisdom); x = ((PLAY_WIDTH + MAIN_WIDTH) / 2) - (XTextWidth(dataFont, wisdom, len) / 2); - nextFrame = frame + 10; + nextFrame = frame + 5; first = False; } @@ -508,11 +508,11 @@ if (noSound == False) playSoundFile("key", 60); DrawText(display, window, x, y, dataFont, red, wisdom, i); - nextFrame = frame + 30; + nextFrame = frame + 15; i++; if (i > len) - SetPresentWait(PRESENT_SPECIAL_TEXT2, frame + 700); + SetPresentWait(PRESENT_SPECIAL_TEXT2, frame + 150); } } @@ -542,7 +542,7 @@ len = strlen(wisdom2); x = ((PLAY_WIDTH + MAIN_WIDTH) / 2) - (XTextWidth(dataFont, wisdom2, len) / 2); - nextFrame = frame + 10; + nextFrame = frame + 5; first = False; } @@ -551,11 +551,11 @@ if (noSound == False) playSoundFile("key", 60); DrawText(display, window, x, y, dataFont, red, wisdom2, i); - nextFrame = frame + 30; + nextFrame = frame + 15; i++; if (i > len) - SetPresentWait(PRESENT_SPECIAL_TEXT3, frame + 700); + SetPresentWait(PRESENT_SPECIAL_TEXT3, frame + 150); } } @@ -584,7 +584,7 @@ len = strlen(wisdom3); x = ((PLAY_WIDTH + MAIN_WIDTH) / 2) - (XTextWidth(dataFont, wisdom3, len) / 2); - nextFrame = frame + 10; + nextFrame = frame + 5; first = False; } @@ -593,11 +593,11 @@ if (noSound == False) playSoundFile("key", 60); DrawText(display, window, x, y, dataFont, red, wisdom3, i); - nextFrame = frame + 30; + nextFrame = frame + 15; i++; if (i > len) - SetPresentWait(PRESENT_CLEAR, frame + 800); + SetPresentWait(PRESENT_CLEAR, frame + 200); } } @@ -642,7 +642,7 @@ yb -= 10; if (yt > ((PLAY_HEIGHT + MAIN_HEIGHT) / 2)) - SetPresentWait(PRESENT_FINISH, frame + 20); + SetPresentWait(PRESENT_FINISH, frame + 10); nextFrame = frame + 20; } ------------------------ >8 ---- CUT HERE ---- 8< -------------------------- Cheers Justin Kibell - UNIX Administrator - RMIT Comm. Eng. - rcojk@co.rmit.edu.au Ph: 9282 2456 - System Administrator - RMIT C.A.T.T. - jck@catt.rmit.edu.au