001 package com.croftsoft.core.util.loop; 002 003 import com.croftsoft.core.animation.updater.FrameRateUpdater; 004 import com.croftsoft.core.math.MathConstants; 005 006 /********************************************************************* 007 * Uses windowed averaging to estimate the target loop delay. 008 * 009 * The window size starts off small to grows to some maximum size. 010 * Any pause greater than one second will cause a window size reset. 011 * 012 * @version 013 * $Date: 2008/04/19 21:27:13 $ 014 * @since 015 * 2003-09-29 016 * @author 017 * <a href="https://www.croftsoft.com/">David Wallace Croft</a> 018 *********************************************************************/ 019 020 public final class WindowedLoopGovernor 021 implements LoopGovernor 022 ////////////////////////////////////////////////////////////////////// 023 ////////////////////////////////////////////////////////////////////// 024 { 025 026 private static final int DEFAULT_MAX_WINDOW_SIZE = 100; 027 028 private static final long DEFAULT_RESET_TIME_NANOS 029 = MathConstants.NANOSECONDS_PER_SECOND; 030 031 // 032 033 private final long periodNanos; 034 035 private final int maxWindowSize; 036 037 private final long resetTimeNanos; 038 039 private final long [ ] nonDelayTimes; 040 041 // 042 043 private int index; 044 045 private int windowSize; 046 047 private long delayMillis; 048 049 private int delayNanos; 050 051 private long previousTimeNanos; 052 053 private long totalDelayNanos; 054 055 private long sumNonDelayTimes; 056 057 ////////////////////////////////////////////////////////////////////// 058 ////////////////////////////////////////////////////////////////////// 059 060 public static void main ( String [ ] args ) 061 throws Exception 062 ////////////////////////////////////////////////////////////////////// 063 { 064 LoopGovernor loopGovernor = new WindowedLoopGovernor ( 85.0 ); 065 066 FrameRateUpdater frameRateUpdater = new FrameRateUpdater ( true ); 067 068 for ( int i = 0; i < 10000; i++ ) 069 { 070 loopGovernor.govern ( ); 071 072 frameRateUpdater.update ( null ); 073 } 074 } 075 076 ////////////////////////////////////////////////////////////////////// 077 // constructor methods 078 ////////////////////////////////////////////////////////////////////// 079 080 /********************************************************************* 081 * Main constructor. 082 *********************************************************************/ 083 public WindowedLoopGovernor ( 084 long periodNanos, 085 int maxWindowSize, 086 long resetTimeNanos ) 087 ////////////////////////////////////////////////////////////////////// 088 { 089 if ( periodNanos < 1 ) 090 { 091 throw new IllegalArgumentException ( "periodNanos < 1" ); 092 } 093 094 this.periodNanos = periodNanos; 095 096 if ( maxWindowSize < 1 ) 097 { 098 throw new IllegalArgumentException ( "maxWindowSize < 1" ); 099 } 100 101 this.maxWindowSize = maxWindowSize; 102 103 if ( resetTimeNanos < 1 ) 104 { 105 throw new IllegalArgumentException ( "resetTimeNanos < 1" ); 106 } 107 108 this.resetTimeNanos = resetTimeNanos; 109 110 nonDelayTimes = new long [ maxWindowSize ]; 111 112 delayMillis 113 = periodNanos / MathConstants.NANOSECONDS_PER_MILLISECOND; 114 115 delayNanos = ( int ) 116 ( periodNanos % MathConstants.NANOSECONDS_PER_MILLISECOND ); 117 118 totalDelayNanos = periodNanos; 119 } 120 121 /********************************************************************* 122 * Convenience constructor. 123 * 124 * @param frequency 125 * 126 * The targeted loop frequency in loops per second. 127 *********************************************************************/ 128 public WindowedLoopGovernor ( double frequency ) 129 ////////////////////////////////////////////////////////////////////// 130 { 131 this ( 132 ( long ) ( MathConstants.NANOSECONDS_PER_SECOND / frequency ), 133 DEFAULT_MAX_WINDOW_SIZE, 134 DEFAULT_RESET_TIME_NANOS ); 135 } 136 137 ////////////////////////////////////////////////////////////////////// 138 ////////////////////////////////////////////////////////////////////// 139 140 public void govern ( ) 141 throws InterruptedException 142 ////////////////////////////////////////////////////////////////////// 143 { 144 long currentTimeNanos = System.nanoTime ( ); 145 146 long nonDelayTime 147 = currentTimeNanos - previousTimeNanos - totalDelayNanos; 148 149 previousTimeNanos = currentTimeNanos; 150 151 long oldNonDelayTime = nonDelayTimes [ index ]; 152 153 nonDelayTimes [ index ] = nonDelayTime; 154 155 sumNonDelayTimes += nonDelayTime; 156 157 index = ( index + 1 ) % maxWindowSize; 158 159 if ( nonDelayTime > resetTimeNanos ) 160 { 161 windowSize = 0; 162 163 sumNonDelayTimes = 0; 164 165 Thread.sleep ( delayMillis, delayNanos ); 166 167 return; 168 } 169 170 if ( windowSize == maxWindowSize ) 171 { 172 sumNonDelayTimes -= oldNonDelayTime; 173 } 174 else 175 { 176 windowSize++; 177 } 178 179 long averageNonDelayTime = sumNonDelayTimes / windowSize; 180 181 totalDelayNanos = periodNanos - averageNonDelayTime; 182 183 if ( totalDelayNanos < 0 ) 184 { 185 totalDelayNanos = 0; 186 } 187 188 delayMillis 189 = totalDelayNanos / MathConstants.NANOSECONDS_PER_MILLISECOND; 190 191 delayNanos = ( int ) 192 ( totalDelayNanos % MathConstants.NANOSECONDS_PER_MILLISECOND ); 193 194 Thread.sleep ( delayMillis, delayNanos ); 195 } 196 197 ////////////////////////////////////////////////////////////////////// 198 ////////////////////////////////////////////////////////////////////// 199 }