001: /*
002: * $RCSfile: FPSCounter.java,v $
003: *
004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * - Redistribution of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * - Redistribution in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * Neither the name of Sun Microsystems, Inc. or the names of
019: * contributors may be used to endorse or promote products derived
020: * from this software without specific prior written permission.
021: *
022: * This software is provided "AS IS," without a warranty of any
023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
034: * POSSIBILITY OF SUCH DAMAGES.
035: *
036: * You acknowledge that this software is not designed, licensed or
037: * intended for use in the design, construction, operation or
038: * maintenance of any nuclear facility.
039: *
040: * $Revision: 1.2 $
041: * $Date: 2007/02/09 17:21:38 $
042: * $State: Exp $
043: */
044:
045: package org.jdesktop.j3d.examples.fps_counter;
046:
047: import javax.media.j3d.*;
048: import javax.vecmath.*;
049: import java.text.*;
050:
051: /** This behavior calculates the frame rate and average frame rate of a
052: * Java3D application.
053: * The behavior sets itself up to wakeup every time a new frame is rendered.
054: *
055: * <p> The HotSpot(tm) compiler performs some initial optimizations before
056: * running at optimal speed. Frame rates measured during this warmup period
057: * will be inaccurate and not indicative of the true performance of the the
058: * application. Therefore, before beginning the frame rate computation,
059: * the frame counter waits for a fixed time period to allow the HotSpot(tm)
060: * compiler to stablilize.
061: *
062: * <p> To avoid computing the frame rate too frequently (which would also
063: * hamper rendering performance), the frame counter only computes the frame
064: * rate at fixed time intervals. The default sampling duration is 10 seconds.
065: * After waiting for the warmup period, the frame counter needs to calibrate
066: * itself. It computes the number of frames rendered during the sampling
067: * period. After doing this calibration, the frame counter reports the frame
068: * rate after these many frames are rendered. It also reports the average
069: * frame rate after a fixed number of sampling intervals (the default is 5).
070: *
071: * <p>The frame counter can be set up to run for a fixed number of sampling
072: * intervals or to run indefinitely. The defaultis to run indefinitely.
073: */
074:
075: public class FPSCounter extends Behavior {
076: // Wakeup condition - framecount = 0 -> wakeup on every frame
077: WakeupOnElapsedFrames FPSwakeup = new WakeupOnElapsedFrames(0);
078:
079: // Do calibration for these many millisec
080: private static final long testduration = 1000;
081:
082: // Report frame rate after every sampleduration milliseconds
083: private static final long sampleduration = 10000;
084:
085: // Flag to indicate that it is time to (re)calibrate
086: private boolean doCalibration = true;
087:
088: // Flag to indicate the counter has started
089: private boolean startup = true;
090:
091: // Wait for HotSpot compiler to perform optimizations
092: private boolean warmup = true;
093:
094: // Time to wait for HotSpot compiler to stabilize (in milliseconds)
095: private long warmupTime = 20000;
096:
097: // Counter for number of frames rendered
098: private int numframes = 0;
099:
100: // Report frame rate after maxframe number of frames have been rendered
101: private int maxframes = 1;
102:
103: // Variables to keep track of elapsed time
104: private long startuptime = 0;
105: private long currtime = 0;
106: private long lasttime = 0;
107: private long deltatime;
108:
109: // Run indefinitely or for a fixed duration
110: private boolean finiteLoop = false;
111:
112: // No. of sampling intervals to run for if not running indefinitely
113: private long maxLoops;
114:
115: // No. of sampling intervals run for so far
116: private long numLoops = 0;
117:
118: // Total number of frames rendered so far
119: private int sumFrames = 0;
120:
121: // Total time since last reporting of average frame rate
122: private long sumTimes = 0;
123:
124: // Counts no. of sampling intervals
125: private int loop = 0;
126:
127: // Average frame rate is reported after loopCount number of
128: // sampling intervals
129: private int loopCount = 5;
130: private double sumFps = 0.0;
131:
132: private String symbol[] = { "\\", "|", "|", "/", "-", "|", "-" };
133: int index = 0;
134: private NumberFormat nf = null;
135:
136: public FPSCounter() {
137: setEnable(true);
138: nf = NumberFormat.getNumberInstance();
139: }
140:
141: /**
142: * Called to init the behavior
143: */
144: public void initialize() {
145: // Set the trigger for the behavior to wakeup on every frame rendered
146: wakeupOn(FPSwakeup);
147: }
148:
149: /**
150: * Called every time the behavior is activated
151: */
152: public void processStimulus(java.util.Enumeration critera) {
153: // Apply calibration algorithm to determine number of frames to
154: // wait before computing frames per second.
155: // sampleduration = 10000 -> to run test, pass for 10 seconds.
156:
157: if (doCalibration) { // start calibration
158: if (startup) {
159: // Record time at which the behavior was first invoked
160: startuptime = System.currentTimeMillis();
161: startup = false;
162: } else if (warmup) { // Wait for the system to stabilize.
163: System.out.print("\rFPSCounter warming up..."
164: + symbol[(index++) % symbol.length]);
165: currtime = System.currentTimeMillis();
166: deltatime = currtime - startuptime;
167: if (deltatime > warmupTime) {
168: // Done waiting for warmup
169: warmup = false;
170: lasttime = System.currentTimeMillis();
171: System.out
172: .println("\rFPSCounter warming up...Done");
173: }
174: } else {
175: numframes += 1;
176: // Wait till at least maxframe no. of frames have been rendered
177: if (numframes >= maxframes) {
178: currtime = System.currentTimeMillis();
179: deltatime = currtime - lasttime;
180: // Do the calibration for testduration no. of millisecs
181: if (deltatime > testduration) {
182: // Compute total no. of frames rendered so far in the
183: // current sampling duration
184: maxframes = (int) Math
185: .ceil((double) numframes
186: * ((double) sampleduration / (double) deltatime));
187:
188: // Done with calibration
189: doCalibration = false;
190: // reset the value for the measurement
191: numframes = 0;
192: lasttime = System.currentTimeMillis();
193: } else {
194: // Need to run the calibration routine for some more
195: // time. Increase the no. of frames to be rendered
196: maxframes *= 2;
197: }
198: }
199: }
200: } else { // do the measurement
201: numframes += 1;
202: if (numframes >= maxframes) {
203: currtime = System.currentTimeMillis();
204: deltatime = currtime - lasttime;
205: // time is in millisec, so multiply by 1000 to get frames/sec
206: double fps = (double) numframes
207: / ((double) deltatime / 1000.0);
208:
209: System.out.println("Frame Rate : \n\tNo. of frames : "
210: + numframes + "\n\tTime : "
211: + ((double) deltatime / 1000.0) + " sec."
212: + "\n\tFrames/sec : " + nf.format(fps));
213:
214: // Calculate average frame rate
215: sumFrames += numframes;
216: sumTimes += deltatime;
217: sumFps += fps;
218: loop++;
219: if (loop >= loopCount) {
220: double avgFps = (double) sumFrames * 1000.0
221: / (double) sumTimes;
222: double ravgFps = sumFps / (double) loopCount;
223: System.out.println("Aggregate frame rate "
224: + nf.format(avgFps) + " frames/sec");
225: System.out.println("Average frame rate "
226: + nf.format(ravgFps) + " frames/sec");
227: numLoops++;
228: if (finiteLoop && numLoops >= maxLoops) {
229: System.out
230: .println("************** The End **************\n");
231: setEnable(false);
232: }
233: loop = 0;
234: sumFps = 0;
235: }
236: numframes = 0;
237: lasttime = System.currentTimeMillis();
238: ;
239: }
240: }
241: // Set the trigger for the behavior
242: wakeupOn(FPSwakeup);
243: }
244:
245: /**
246: * The frame counter waits for some time before computing the
247: * frame rate. This allows the HotSpot compiler to perform
248: * initial optimizations. The amount of time to wait for is set
249: * by this method. The default is 20000 (20 sec)
250: *
251: * @param amount of time to wait for before computing frame rate
252: * (specified in milliseconds)
253: */
254: public void setWarmupTime(long wt) {
255: warmupTime = wt;
256: }
257:
258: /**
259: * Sets the number of sampling intervals to wait for before computing
260: * the average frame rate.
261: * The default is 5.
262: *
263: * @param number of sampling intervals over which to compute frame rate.
264: * A value of 0 implies the average frame rate is computed over one
265: * sampling interval
266: */
267: public void setLoopCount(int lc) {
268: loopCount = lc;
269: }
270:
271: /**
272: * This method sets the number of sampling intervals for which
273: * the frame counter should run.
274: *
275: * @param number of sampling intervals to run for
276: */
277: public void setMaxLoops(int ml) {
278: maxLoops = ml;
279: finiteLoop = true;
280: }
281:
282: }
|