Coverage Report - uk.co.javagear.Throttle
 
Classes in this File Line Coverage Branch Coverage Complexity
Throttle
0% 
0% 
2.375
 
 1  
 /*
 2  
  * Throttle.java
 3  
  *
 4  
  * Java Emulation Framework
 5  
  *
 6  
  * This library contains a framework for creating emulation software.
 7  
  *
 8  
  * Copyright (C) 2002 Erik Duijs (erikduijs@yahoo.com)
 9  
  *
 10  
  * Contributors:
 11  
  * - Julien Freilat
 12  
  * - Arnon Goncalves Cardoso
 13  
  * - S.C. Wong
 14  
  * - Romain Tisserand
 15  
  * - David Raingeard
 16  
  *
 17  
  * This library is free software; you can redistribute it and/or
 18  
  * modify it under the terms of the GNU Lesser General Public
 19  
  * License as published by the Free Software Foundation; either
 20  
  * version 2.1 of the License, or (at your option) any later version.
 21  
  *
 22  
  * This library is distributed in the hope that it will be useful,
 23  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 24  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 25  
  * Lesser General Public License for more details.
 26  
  *
 27  
  * You should have received a copy of the GNU Lesser General Public
 28  
  * License along with this library; if not, write to the Free Software
 29  
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 30  
  */
 31  
 
 32  
 package uk.co.javagear;
 33  
 
 34  
 import org.apache.log4j.Logger;
 35  
 
 36  
 /**
 37  
  * Provides the speed throttling functionality based
 38  
  * on System.currentTimeMillis() and Thread.sleep().
 39  
  * Heavily based on code by Arnon Cardoso.
 40  
  *
 41  
  * @author Erik Duijs
 42  
  * @author Arnon Goncalves Cardoso
 43  
  */
 44  
 public final class Throttle {
 45  
     
 46  
     /**
 47  
      * Throttle, frameskip constants.
 48  
      */
 49  
     static final int MAX_THROTTLE_STEP = 5;
 50  
     
 51  
     /**
 52  
      * Throttle, frameskip constants.
 53  
      */
 54  
     static final int MIN_THROTTLE_STEP = 1;
 55  
     
 56  
     /**
 57  
      * Throttle, frameskip constants.
 58  
      */
 59  
     static final int DEFAULT_THROTTLE_STEP = 1;
 60  
     
 61  
     /**
 62  
      * Throttle, frameskip constants.
 63  
      */
 64  
     static final int DEFAULT_TARGET_FPS = 60;
 65  
     
 66  
     /**
 67  
      * Throttle, frameskip constants.
 68  
      */
 69  
     static final int FRAMES_UNTIL_THROTTLE_RECALC = 40;
 70  
     
 71  
     /**
 72  
      * Throttle, frameskip constants.
 73  
      */
 74  
     static final int MAX_FRAME_SKIP = 5;
 75  
     
 76  
     /**
 77  
      * Throttle, frameskip constants.
 78  
      */
 79  
     static final float MAX_FPS_DEVIATION = 0.10f;
 80  
     
 81  
     /**
 82  
      * Throttle, frameskip constants.
 83  
      */
 84  
     static final boolean TRY_ALT_SKIP_CALC = false;
 85  
     
 86  
     // TODO - Check if all this use of class variables can/should be avoided.
 87  
     
 88  
     /**
 89  
      * Throttle is enabled by default.
 90  
      */
 91  
     static boolean throttle;
 92  
     
 93  
     /**
 94  
      * Auto frame skip.
 95  
      */
 96  
     static boolean autoFS;
 97  
     
 98  
     /**
 99  
      * Frames per second.
 100  
      */
 101  
     static long fps;
 102  
     
 103  
     /** 
 104  
      * Sum frames per second for measuring avg FPS.
 105  
      */
 106  0
     static long sumFPS = 0;
 107  
     
 108  
     /**
 109  
      * Average FPS.
 110  
      */
 111  0
     static float avgFPS = 0f;
 112  
     
 113  
     /**
 114  
      * Amount of sleep time in ms.
 115  
      */
 116  
     static long sleep;
 117  
     
 118  
     /**
 119  
      * The FPS that needs to be throttled to.
 120  
      */
 121  
     static int targetFPS;
 122  
     
 123  
     /**
 124  
      * How fast the throttle changes sleep time.
 125  
      */
 126  
     static int throttleStep;
 127  
     
 128  
     /**
 129  
      * Minimum FPS.
 130  
      */
 131  
     static int minFPS;
 132  
     
 133  
     /**
 134  
      * Maximum FPS.
 135  
      */
 136  
     static int maxFPS;
 137  
     
 138  
     /**
 139  
      * Minimum sleeptime to have effect on the JVM.
 140  
      */
 141  
     static long minimumSleep;
 142  
     
 143  
     /**
 144  
      * Frame skip.
 145  
      */
 146  
     static int fskip;
 147  
     
 148  
     /** 
 149  
      * Amount of ms. of one frame.
 150  
      */
 151  
     static int frameDuration;
 152  
     
 153  
     /**
 154  
      * Time in ms.
 155  
      */
 156  
     static long t;
 157  
     
 158  
     /**
 159  
      * Time in ms.
 160  
      */
 161  
     static long tempT;
 162  
     
 163  
     /**
 164  
      * Framenumber relative to last recalc.
 165  
      */
 166  
     static int frameNumber;
 167  
     
 168  
     /**
 169  
      * Count recalc for measuring avg fps.
 170  
      */
 171  0
     static long recalcCount = 0;
 172  
     
 173  
     /**
 174  
      * The thread to throttle.
 175  
      */
 176  
     static Thread thread;
 177  
     
 178  
     /**
 179  
      * This class only provides static methods and variables.
 180  
      */
 181  0
     private Throttle() {
 182  0
     }
 183  
     
 184  
     /**
 185  
      * Initialize the throttle.
 186  
      *
 187  
      * @param fps the target number of frames persecond.
 188  
      * @param thread the thread to throttle.
 189  
      */
 190  
     public static void init(int fps, Thread thread) {
 191  0
         targetFPS = fps;
 192  0
         Throttle.thread = thread;
 193  0
         throttle = true;
 194  0
         autoFS = false; // Don't automatically skip frames
 195  
         //fskip = 0;
 196  0
         throttleStep = DEFAULT_THROTTLE_STEP;
 197  0
         frameNumber = 0;
 198  0
         recalcCount = 0;
 199  0
         sumFPS = 0;
 200  0
         avgFPS = 0f;
 201  0
         Throttle.fps = 0;
 202  
         
 203  
         // This part is weird... Its intention is to set the minimumSleep variable, which is the
 204  
         // minimum amount of milliseconds that the throttle may slow down 1 frame.
 205  
         // Why? Because, depending on the VM and/or OS (not sure yet), there is a minimum value
 206  
         // for Thread.Sleep(ms) to have an effect.
 207  
         // When running M$ JRE on Win2k, this value is incredibly high (11 ms.),
 208  
         // Running SUN JRE on Win2k, it's better (5 ms.).
 209  
         // It would be logical to look for vm.vendor, but this is not allowed in an applet :o(
 210  
         // Now, I look for os.name (which is allowed) and make use of the fact that the M$ VM
 211  
         // thinks that Windows 2000 is NT, where Sun seems to be better at distinguishing between
 212  
         // M$'s OS-es (!).
 213  
         // This mega dirty hack works on win2k, but probably not on NT.
 214  
         // ....This whole throttling in java gives me a headache....
 215  0
         String osname = System.getProperty("os.name");
 216  0
         minimumSleep = osname.endsWith("NT") ? 11 : 5;
 217  0
         sleep = minimumSleep;
 218  0
         throttleStep = DEFAULT_THROTTLE_STEP;
 219  0
         minFPS = targetFPS - (int) ((float) (targetFPS * MAX_FPS_DEVIATION));
 220  0
         if (minFPS == targetFPS) {
 221  0
             minFPS = targetFPS - 1;
 222  
         }
 223  0
         maxFPS = targetFPS + (int) ((float) (targetFPS * MAX_FPS_DEVIATION));
 224  0
         if (maxFPS == targetFPS) {
 225  0
             maxFPS = targetFPS + 1;
 226  
         }
 227  
         
 228  0
         frameDuration = 1000 / targetFPS;
 229  0
         Throttle.fps = (long) targetFPS;
 230  0
         t = System.currentTimeMillis();
 231  0
     }
 232  
     
 233  
     /**
 234  
      * Call this method each frame.
 235  
      * Here the actual throttling takes place.
 236  
      */
 237  
     public static void throttle() {
 238  
         // Try slow down to the machine's original speed
 239  0
         if (throttle & ((frameNumber % throttleStep) == 0)) {
 240  
             try {
 241  0
                 thread.sleep(sleep);
 242  0
             } catch (InterruptedException ie) {
 243  0
                 Logger.getLogger(Throttle.class).error("Thread was interrupted.", ie);
 244  0
             }
 245  
         }
 246  
         
 247  0
         if (frameNumber < FRAMES_UNTIL_THROTTLE_RECALC) {
 248  0
             frameNumber++;
 249  
         } else {
 250  0
             frameNumber = 0;
 251  0
             recalcTiming();
 252  
         }
 253  0
     }
 254  
     
 255  
     /**
 256  
      * Get current sleep time in ms.
 257  
      *
 258  
      * @return current sleep time in ms.
 259  
      */
 260  
     public static long getSleep() {
 261  0
         return sleep;
 262  
     }
 263  
     
 264  
     /**
 265  
      * Returns <code>true</code> if a frame needs to be skipped, <code>false</code> otherwise.
 266  
      *
 267  
      * @return <code>true</code> if a frame needs to be skipped, <code>false</code> otherwise.
 268  
      */
 269  
     public static boolean skipFrame() {
 270  0
         return !((fskip == 0) || ((frameNumber % (fskip + 1)) == 0));
 271  
     }
 272  
     
 273  
     /**
 274  
      * Enable/disable automatic frame skip.
 275  
      *
 276  
      * @param enable <code>true</code> to enable automatic frame skip, <code>false</code> 
 277  
      *    to disable.
 278  
      */
 279  
     public static void enableAutoFrameSkip(boolean enable) {
 280  0
         autoFS = enable;
 281  0
     }
 282  
     
 283  
     /**
 284  
      * Returns <code>true</code> if automatic frame skip is enabled, <code>false</code> otherwise.
 285  
      *
 286  
      * @return <code>true</code> if automatic frame skip is enabled, <code>false</code> otherwise.
 287  
      */
 288  
     public static boolean isAutoFrameSkip() {
 289  0
         return autoFS;
 290  
     }
 291  
     
 292  
     /**
 293  
      * Enable throttle.
 294  
      *
 295  
      * @param enable <code>true</code> to enable throttle, <code>false</code> 
 296  
      *    to disable.
 297  
      */
 298  
     public static void enable(boolean enable) {
 299  0
         throttle = enable;
 300  0
     }
 301  
     
 302  
     /**
 303  
      * Returns <code>true</code> if throttling is enabled and <code>false</code> otherwise.
 304  
      *
 305  
      * @return <code>true</code> if throttling is enabled and <code>false</code> otherwise.
 306  
      */
 307  
     public static boolean isEnabled() {
 308  0
         return throttle;
 309  
     }
 310  
     
 311  
     /**
 312  
      * Set the amount of frames to skip.
 313  
      *
 314  
      * @param skip the number of frames to skip.
 315  
      */
 316  
     public static void setFrameSkip(int skip) {
 317  0
         fskip = skip;
 318  0
     }
 319  
     
 320  
     /**
 321  
      * Get current amount of frames to be skipped.
 322  
      *
 323  
      * @return the current number of frames to skip.
 324  
      */
 325  
     public static int getFrameSkip() {
 326  0
         return fskip;
 327  
     }
 328  
     
 329  
     /**
 330  
      * Get current FPS.
 331  
      *
 332  
      * @return the current FPS.
 333  
      */
 334  
     public static int getFPS() {
 335  0
         return (int) fps;
 336  
     }
 337  
     
 338  
     /**
 339  
      * Get the target FPS.
 340  
      *
 341  
      * @return the target FPS.
 342  
      */
 343  
     public static int getTargetFPS() {
 344  0
         return targetFPS;
 345  
     }
 346  
     
 347  
     /**
 348  
      * Get the average FPS.
 349  
      *
 350  
      * @return the average FPS.
 351  
      */
 352  
     public static float getAverageFPS() {
 353  0
         return avgFPS;
 354  
     }
 355  
     
 356  
     public static int getPercentage() {
 357  0
         return (int) ((avgFPS / targetFPS) * 100.0);
 358  
     }
 359  
     
 360  
     /**
 361  
      * Called after <code>FRAMES_UNTIL_THROTTLE_RECALC</code> is reached.
 362  
      * Here the sleep time and auto frame skip is re-evaluated.
 363  
      */
 364  
     private static void recalcTiming() {
 365  0
         tempT = System.currentTimeMillis();
 366  0
         fps = 1000 / ((tempT - t) / FRAMES_UNTIL_THROTTLE_RECALC);
 367  0
         t = tempT;
 368  
         
 369  0
         recalcCount = recalcCount + 1;
 370  0
         sumFPS = sumFPS + fps;
 371  
         
 372  0
         avgFPS = (float) ((int) ((sumFPS * 100) / recalcCount)) / 100;
 373  
         
 374  0
         if (throttle) {
 375  0
             if (fps < minFPS) {
 376  0
                 if (sleep > minimumSleep) {
 377  
                     if (TRY_ALT_SKIP_CALC) {
 378  
                         sleep -= (targetFPS - fps) / (targetFPS - minFPS);
 379  
                     } else {
 380  0
                         sleep--;
 381  
                     }
 382  
                 }
 383  0
                 if (sleep <= minimumSleep) {
 384  0
                     throttleStep++;
 385  0
                     if (throttleStep > MAX_THROTTLE_STEP) {
 386  0
                         throttleStep = MAX_THROTTLE_STEP;
 387  0
                         if (autoFS) {
 388  0
                             throttleStep = MIN_THROTTLE_STEP;
 389  0
                             fskip++;
 390  0
                             if (fskip > MAX_FRAME_SKIP) {
 391  0
                                 fskip = MAX_FRAME_SKIP;
 392  0
                                 throttleStep = MAX_THROTTLE_STEP;
 393  
                             }
 394  
                         }
 395  
                     } else {
 396  
                         if (TRY_ALT_SKIP_CALC) {
 397  
                             sleep += minimumSleep;
 398  
                         } else {
 399  0
                             sleep = (1000 / targetFPS - 1);
 400  
                         }
 401  
                     }
 402  
                 }
 403  0
             } else if (fps > maxFPS) {
 404  
                 if (TRY_ALT_SKIP_CALC) {
 405  
                     sleep += (fps - targetFPS) / (maxFPS - targetFPS);
 406  
                 } else {
 407  0
                     sleep++;
 408  
                 }
 409  0
                 if (sleep > (1000 / targetFPS - 1)) {
 410  0
                     throttleStep--;
 411  0
                     if (throttleStep < MIN_THROTTLE_STEP) {
 412  0
                         throttleStep = MIN_THROTTLE_STEP;
 413  0
                         if (autoFS) {
 414  0
                             throttleStep = MAX_THROTTLE_STEP;
 415  0
                             fskip--;
 416  0
                             if (fskip < 0) {
 417  0
                                 fskip = 0;
 418  0
                                 throttleStep = MIN_THROTTLE_STEP;
 419  
                             }
 420  
                         }
 421  
                     } else {
 422  
                         if (TRY_ALT_SKIP_CALC) {
 423  
                             sleep -= minimumSleep;
 424  
                             if (sleep < minimumSleep) {
 425  
                                 sleep = minimumSleep;
 426  
                             }
 427  
                         } else {
 428  0
                             sleep = minimumSleep;
 429  
                         }
 430  
                     }
 431  
                 }
 432  
             }
 433  
         }
 434  0
     }
 435  
     
 436  
 }